1 // Copyright 2015 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.chrome.browser.contextualsearch; 6 7 import android.text.TextUtils; 8 import android.text.format.DateUtils; 9 import android.util.Pair; 10 11 import androidx.annotation.IntDef; 12 import androidx.annotation.Nullable; 13 14 import org.chromium.base.metrics.RecordHistogram; 15 import org.chromium.base.metrics.RecordUserAction; 16 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.PanelState; 17 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.StateChangeReason; 18 import org.chromium.chrome.browser.contextualsearch.ResolvedSearchTerm.CardTag; 19 import org.chromium.chrome.browser.sync.AndroidSyncSettings; 20 21 import java.lang.annotation.Retention; 22 import java.lang.annotation.RetentionPolicy; 23 import java.util.Collections; 24 import java.util.HashMap; 25 import java.util.Map; 26 import java.util.regex.Pattern; 27 28 /** 29 * Centralizes UMA data collection for Contextual Search. All calls must be made from the UI thread. 30 */ 31 public class ContextualSearchUma { 32 // Constants to use for the original selection gesture 33 private static final boolean LONG_PRESS = false; 34 private static final boolean TAP = true; 35 36 /** A pattern to determine if text contains any whitespace. */ 37 private static final Pattern CONTAINS_WHITESPACE_PATTERN = Pattern.compile("\\s"); 38 39 // Constants used to log UMA "enum" histograms about the Contextual Search's preference state. 40 @IntDef({Preference.UNINITIALIZED, Preference.ENABLED, Preference.DISABLED}) 41 @Retention(RetentionPolicy.SOURCE) 42 private @interface Preference { 43 int UNINITIALIZED = 0; 44 int ENABLED = 1; 45 int DISABLED = 2; 46 int NUM_ENTRIES = 3; 47 } 48 49 // Constants used to log UMA "enum" histograms about whether search results were seen. 50 @IntDef({Results.SEEN, Results.NOT_SEEN}) 51 @Retention(RetentionPolicy.SOURCE) 52 private @interface Results { 53 int SEEN = 0; 54 int NOT_SEEN = 1; 55 int NUM_ENTRIES = 2; 56 } 57 58 // Constants used to log UMA "enum" histograms about whether the selection is valid. 59 @IntDef({Selection.VALID, Selection.INVALID}) 60 @Retention(RetentionPolicy.SOURCE) 61 private @interface Selection { 62 int VALID = 0; 63 int INVALID = 1; 64 int NUM_ENTRIES = 2; 65 } 66 67 // Constants used to log UMA "enum" histograms about a request's outcome. 68 @IntDef({Request.NOT_FAILED, Request.FAILED}) 69 @Retention(RetentionPolicy.SOURCE) 70 private @interface Request { 71 int NOT_FAILED = 0; 72 int FAILED = 1; 73 int NUM_ENTRIES = 2; 74 } 75 76 // Constants used to log UMA "enum" histograms about the panel's state transitions. 77 // Entry code: first entry into CLOSED. 78 @IntDef({EnterClosedFrom.OTHER, EnterClosedFrom.PEEKED_BACK_PRESS, 79 EnterClosedFrom.PEEKED_BASE_PAGE_SCROLL, EnterClosedFrom.PEEKED_TEXT_SELECT_TAP, 80 EnterClosedFrom.EXPANDED_BACK_PRESS, EnterClosedFrom.EXPANDED_BASE_PAGE_TAP, 81 EnterClosedFrom.EXPANDED_FLING, EnterClosedFrom.MAXIMIZED_BACK_PRESS, 82 EnterClosedFrom.MAXIMIZED_FLING, EnterClosedFrom.MAXIMIZED_TAB_PROMOTION, 83 EnterClosedFrom.MAXIMIZED_SERP_NAVIGATION}) 84 @Retention(RetentionPolicy.SOURCE) 85 private @interface EnterClosedFrom { 86 int OTHER = 0; 87 int PEEKED_BACK_PRESS = 1; 88 int PEEKED_BASE_PAGE_SCROLL = 2; 89 int PEEKED_TEXT_SELECT_TAP = 3; 90 int EXPANDED_BACK_PRESS = 4; 91 int EXPANDED_BASE_PAGE_TAP = 5; 92 int EXPANDED_FLING = 6; 93 int MAXIMIZED_BACK_PRESS = 7; 94 int MAXIMIZED_FLING = 8; 95 int MAXIMIZED_TAB_PROMOTION = 9; 96 int MAXIMIZED_SERP_NAVIGATION = 10; 97 int NUM_ENTRIES = 11; 98 } 99 100 // Entry code: first entry into PEEKED. 101 @IntDef({EnterPeekedFrom.OTHER, EnterPeekedFrom.CLOSED_TEXT_SELECT_TAP, 102 EnterPeekedFrom.CLOSED_EXT_SELECT_LONG_PRESS, EnterPeekedFrom.PEEKED_TEXT_SELECT_TAP, 103 EnterPeekedFrom.PEEKED_TEXT_SELECT_LONG_PRESS, EnterPeekedFrom.EXPANDED_SEARCH_BAR_TAP, 104 EnterPeekedFrom.EXPANDED_SWIPE, EnterPeekedFrom.EXPANDED_FLING, 105 EnterPeekedFrom.MAXIMIZED_SWIPE, EnterPeekedFrom.MAXIMIZED_FLING}) 106 @Retention(RetentionPolicy.SOURCE) 107 private @interface EnterPeekedFrom { 108 int OTHER = 0; 109 int CLOSED_TEXT_SELECT_TAP = 1; 110 int CLOSED_EXT_SELECT_LONG_PRESS = 2; 111 int PEEKED_TEXT_SELECT_TAP = 3; 112 int PEEKED_TEXT_SELECT_LONG_PRESS = 4; 113 int EXPANDED_SEARCH_BAR_TAP = 5; 114 int EXPANDED_SWIPE = 6; 115 int EXPANDED_FLING = 7; 116 int MAXIMIZED_SWIPE = 8; 117 int MAXIMIZED_FLING = 9; 118 int NUM_ENTRIES = 10; 119 } 120 121 // Entry code: first entry into EXPANDED. 122 @IntDef({EnterExpandedFrom.OTHER, EnterExpandedFrom.PEEKED_SEARCH_BAR_TAP, 123 EnterExpandedFrom.PEEKED_SWIPE, EnterExpandedFrom.PEEKED_FLING, 124 EnterExpandedFrom.MAXIMIZED_SWIPE, EnterExpandedFrom.MAXIMIZED_FLING}) 125 @Retention(RetentionPolicy.SOURCE) 126 private @interface EnterExpandedFrom { 127 int OTHER = 0; 128 int PEEKED_SEARCH_BAR_TAP = 1; 129 int PEEKED_SWIPE = 2; 130 int PEEKED_FLING = 3; 131 int MAXIMIZED_SWIPE = 4; 132 int MAXIMIZED_FLING = 5; 133 int NUM_ENTRIES = 6; 134 } 135 136 // Entry code: first entry into MAXIMIZED. 137 @IntDef({EnterMaximizedFrom.OTHER, EnterMaximizedFrom.PEEKED_SWIPE, 138 EnterMaximizedFrom.PEEKED_FLING, EnterMaximizedFrom.EXPANDED_SWIPE, 139 EnterMaximizedFrom.EXPANDED_FLING, EnterMaximizedFrom.EXPANDED_SERP_NAVIGATION}) 140 @Retention(RetentionPolicy.SOURCE) 141 private @interface EnterMaximizedFrom { 142 int OTHER = 0; 143 int PEEKED_SWIPE = 1; 144 int PEEKED_FLING = 2; 145 int EXPANDED_SWIPE = 3; 146 int EXPANDED_FLING = 4; 147 int EXPANDED_SERP_NAVIGATION = 5; 148 int NUM_ENTRIES = 6; 149 } 150 151 // Exit code: first exit from CLOSED (or UNDEFINED). 152 @IntDef({ExitClosedTo.OTHER, ExitClosedTo.PEEKED_TEXT_SELECT_TAP, 153 ExitClosedTo.PEEKED_TEXT_SELECT_LONG_PRESS}) 154 @Retention(RetentionPolicy.SOURCE) 155 private @interface ExitClosedTo { 156 int OTHER = 0; 157 int PEEKED_TEXT_SELECT_TAP = 1; 158 int PEEKED_TEXT_SELECT_LONG_PRESS = 2; 159 int NUM_ENTRIES = 3; 160 } 161 162 // Exit code: first exit from PEEKED. 163 @IntDef({ExitPeekedTo.OTHER, ExitPeekedTo.CLOSED_BACK_PRESS, 164 ExitPeekedTo.CLOSED_BASE_PAGE_SCROLL, ExitPeekedTo.CLOSED_TEXT_SELECT_TAP, 165 ExitPeekedTo.PEEKED_TEXT_SELECT_TAP, ExitPeekedTo.PEEKED_TEXT_SELECT_LONG_PRESS, 166 ExitPeekedTo.EXPANDED_SEARCH_BAR_TAP, ExitPeekedTo.EXPANDED_SWIPE, 167 ExitPeekedTo.EXPANDED_FLING, ExitPeekedTo.MAXIMIZED_SWIPE, 168 ExitPeekedTo.MAXIMIZED_FLING}) 169 @Retention(RetentionPolicy.SOURCE) 170 private @interface ExitPeekedTo { 171 int OTHER = 0; 172 int CLOSED_BACK_PRESS = 1; 173 int CLOSED_BASE_PAGE_SCROLL = 2; 174 int CLOSED_TEXT_SELECT_TAP = 3; 175 int PEEKED_TEXT_SELECT_TAP = 4; 176 int PEEKED_TEXT_SELECT_LONG_PRESS = 5; 177 int EXPANDED_SEARCH_BAR_TAP = 6; 178 int EXPANDED_SWIPE = 7; 179 int EXPANDED_FLING = 8; 180 int MAXIMIZED_SWIPE = 9; 181 int MAXIMIZED_FLING = 10; 182 int NUM_ENTRIES = 11; 183 } 184 185 // Exit code: first exit from EXPANDED. 186 @IntDef({ExitExpandedTo.OTHER, ExitExpandedTo.CLOSED_BACK_PRESS, 187 ExitExpandedTo.CLOSED_BASE_PAGE_TAP, ExitExpandedTo.CLOSED_FLING, 188 ExitExpandedTo.PEEKED_SEARCH_BAR_TAP, ExitExpandedTo.PEEKED_SWIPE, 189 ExitExpandedTo.PEEKED_FLING, ExitExpandedTo.MAXIMIZED_SWIPE, 190 ExitExpandedTo.MAXIMIZED_FLING, ExitExpandedTo.MAXIMIZED_SERP_NAVIGATION}) 191 @Retention(RetentionPolicy.SOURCE) 192 private @interface ExitExpandedTo { 193 int OTHER = 0; 194 int CLOSED_BACK_PRESS = 1; 195 int CLOSED_BASE_PAGE_TAP = 2; 196 int CLOSED_FLING = 3; 197 int PEEKED_SEARCH_BAR_TAP = 4; 198 int PEEKED_SWIPE = 5; 199 int PEEKED_FLING = 6; 200 int MAXIMIZED_SWIPE = 7; 201 int MAXIMIZED_FLING = 8; 202 int MAXIMIZED_SERP_NAVIGATION = 9; 203 int NUM_ENTRIES = 10; 204 } 205 206 // Exit code: first exit from MAXIMIZED. 207 @IntDef({ExitMaximizedTo.OTHER, ExitMaximizedTo.CLOSED_BACK_PRESS, ExitMaximizedTo.CLOSED_FLING, 208 ExitMaximizedTo.CLOSED_TAB_PROMOTION, ExitMaximizedTo.CLOSED_SERP_NAVIGATION, 209 ExitMaximizedTo.PEEKED_SWIPE, ExitMaximizedTo.PEEKED_FLING, 210 ExitMaximizedTo.EXPANDED_SWIPE, ExitMaximizedTo.EXPANDED_FLING}) 211 @Retention(RetentionPolicy.SOURCE) 212 private @interface ExitMaximizedTo { 213 int OTHER = 0; 214 int CLOSED_BACK_PRESS = 1; 215 int CLOSED_FLING = 2; 216 int CLOSED_TAB_PROMOTION = 3; 217 int CLOSED_SERP_NAVIGATION = 4; 218 int PEEKED_SWIPE = 5; 219 int PEEKED_FLING = 6; 220 int EXPANDED_SWIPE = 7; 221 int EXPANDED_FLING = 8; 222 int NUM_ENTRIES = 9; 223 } 224 225 // Constants used to log UMA "enum" histograms with details about whether search results 226 // were seen, and what the original triggering gesture was. 227 @IntDef({ResultsByGesture.SEEN_FROM_TAP, ResultsByGesture.NOT_SEEN_FROM_TAP, 228 ResultsByGesture.SEEN_FROM_LONG_PRESS, ResultsByGesture.NOT_SEEN_FROM_LONG_PRESS}) 229 @Retention(RetentionPolicy.SOURCE) 230 private @interface ResultsByGesture { 231 int SEEN_FROM_TAP = 0; 232 int NOT_SEEN_FROM_TAP = 1; 233 int SEEN_FROM_LONG_PRESS = 2; 234 int NOT_SEEN_FROM_LONG_PRESS = 3; 235 int NUM_ENTRIES = 4; 236 } 237 238 // Constants used to log UMA "enum" histograms with details about whether search results 239 // were seen, and whether any existing tap suppression heuristics were satisfied. 240 @IntDef({ResultsBySuppression.SEEN_SUPPRESSION_HEURSTIC_SATISFIED, 241 ResultsBySuppression.NOT_SEEN_SUPPRESSION_HEURSTIC_SATISFIED, 242 ResultsBySuppression.SEEN_SUPPRESSION_HEURSTIC_NOT_SATISFIED, 243 ResultsBySuppression.NOT_SEEN_SUPPRESSION_HEURSTIC_NOT_SATISFIED}) 244 @Retention(RetentionPolicy.SOURCE) 245 private @interface ResultsBySuppression { 246 int SEEN_SUPPRESSION_HEURSTIC_SATISFIED = 0; 247 int NOT_SEEN_SUPPRESSION_HEURSTIC_SATISFIED = 1; 248 int SEEN_SUPPRESSION_HEURSTIC_NOT_SATISFIED = 2; 249 int NOT_SEEN_SUPPRESSION_HEURSTIC_NOT_SATISFIED = 3; 250 int NUM_ENTRIES = 4; 251 } 252 253 // Constants used to log UMA "enum" histograms with details about whether search results 254 // were seen, and what the original triggering gesture was. 255 @IntDef({Promo.ENABLED_FROM_TAP, Promo.DISABLED_FROM_TAP, Promo.UNDECIDED_FROM_TAP, 256 Promo.ENABLED_FROM_LONG_PRESS, Promo.DISABLED_FROM_LONG_PRESS, 257 Promo.UNDECIDED_FROM_LONG_PRESS}) 258 @Retention(RetentionPolicy.SOURCE) 259 private @interface Promo { 260 int ENABLED_FROM_TAP = 0; 261 int DISABLED_FROM_TAP = 1; 262 int UNDECIDED_FROM_TAP = 2; 263 int ENABLED_FROM_LONG_PRESS = 3; 264 int DISABLED_FROM_LONG_PRESS = 4; 265 int UNDECIDED_FROM_LONG_PRESS = 5; 266 int NUM_ENTRIES = 6; 267 } 268 269 // Constants used to log UMA "enum" histograms for HTTP / HTTPS. 270 @IntDef({Protocol.IS_HTTP, Protocol.NOT_HTTP}) 271 @Retention(RetentionPolicy.SOURCE) 272 private @interface Protocol { 273 int IS_HTTP = 0; 274 int NOT_HTTP = 1; 275 int NUM_ENTRIES = 2; 276 } 277 278 // Constants used to log UMA "enum" histograms for single / multi-word. 279 @IntDef({ResolvedGranularity.SINGLE_WORD, ResolvedGranularity.MULTI_WORD}) 280 @Retention(RetentionPolicy.SOURCE) 281 private @interface ResolvedGranularity { 282 int SINGLE_WORD = 0; 283 int MULTI_WORD = 1; 284 int NUM_ENTRIES = 2; 285 } 286 287 // Constants used to log UMA "enum" histograms for Quick Answers. 288 @IntDef({QuickAnswerSeen.ACTIVATED_WAS_AN_ANSWER_SEEN, 289 QuickAnswerSeen.ACTIVATED_WAS_AN_ANSWER_NOT_SEEN, 290 QuickAnswerSeen.ACTIVATED_NOT_AN_ANSWER_SEEN, 291 QuickAnswerSeen.ACTIVATED_NOT_AN_ANSWER_NOT_SEEN, QuickAnswerSeen.NOT_ACTIVATED_SEEN, 292 QuickAnswerSeen.NOT_ACTIVATED_NOT_SEEN}) 293 @Retention(RetentionPolicy.SOURCE) 294 private @interface QuickAnswerSeen { 295 int ACTIVATED_WAS_AN_ANSWER_SEEN = 0; 296 int ACTIVATED_WAS_AN_ANSWER_NOT_SEEN = 1; 297 int ACTIVATED_NOT_AN_ANSWER_SEEN = 2; 298 int ACTIVATED_NOT_AN_ANSWER_NOT_SEEN = 3; 299 int NOT_ACTIVATED_SEEN = 4; 300 int NOT_ACTIVATED_NOT_SEEN = 5; 301 int NUM_ENTRIES = 6; 302 } 303 304 // Constants for "Bar Overlap" with triggering gesture, and whether the results were seen. 305 @IntDef({BarOverlapResults.BAR_OVERLAP_RESULTS_SEEN_FROM_TAP, 306 BarOverlapResults.BAR_OVERLAP_RESULTS_NOT_SEEN_FROM_TAP, 307 BarOverlapResults.NO_BAR_OVERLAP_RESULTS_SEEN_FROM_TAP, 308 BarOverlapResults.NO_BAR_OVERLAP_RESULTS_NOT_SEEN_FROM_TAP, 309 BarOverlapResults.BAR_OVERLAP_RESULTS_SEEN_FROM_LONG_PRESS, 310 BarOverlapResults.BAR_OVERLAP_RESULTS_NOT_SEEN_FROM_LONG_PRESS, 311 BarOverlapResults.NO_BAR_OVERLAP_RESULTS_SEEN_FROM_LONG_PRESS, 312 BarOverlapResults.NO_BAR_OVERLAP_RESULTS_NOT_SEEN_FROM_LONG_PRESS}) 313 @Retention(RetentionPolicy.SOURCE) 314 private @interface BarOverlapResults { 315 int BAR_OVERLAP_RESULTS_SEEN_FROM_TAP = 0; 316 int BAR_OVERLAP_RESULTS_NOT_SEEN_FROM_TAP = 1; 317 int NO_BAR_OVERLAP_RESULTS_SEEN_FROM_TAP = 2; 318 int NO_BAR_OVERLAP_RESULTS_NOT_SEEN_FROM_TAP = 3; 319 int BAR_OVERLAP_RESULTS_SEEN_FROM_LONG_PRESS = 4; 320 int BAR_OVERLAP_RESULTS_NOT_SEEN_FROM_LONG_PRESS = 5; 321 int NO_BAR_OVERLAP_RESULTS_SEEN_FROM_LONG_PRESS = 6; 322 int NO_BAR_OVERLAP_RESULTS_NOT_SEEN_FROM_LONG_PRESS = 7; 323 int NUM_ENTRIES = 8; 324 } 325 326 // Constants for quick action intent resolution histogram. 327 @IntDef({QuickActionResolve.FAILED, QuickActionResolve.SINGLE, QuickActionResolve.MULTIPLE}) 328 @Retention(RetentionPolicy.SOURCE) 329 private @interface QuickActionResolve { 330 int FAILED = 0; 331 int SINGLE = 1; 332 int MULTIPLE = 2; 333 int NUM_ENTRIES = 3; 334 } 335 336 // Constants for user permissions histogram. 337 @IntDef({ 338 Permissions.SEND_NOTHING, 339 Permissions.SEND_URL, 340 Permissions.SEND_CONTENT, 341 Permissions.SEND_URL_AND_CONTENT, 342 }) 343 @Retention(RetentionPolicy.SOURCE) 344 private @interface Permissions { 345 int SEND_NOTHING = 0; 346 int SEND_URL = 1; 347 int SEND_CONTENT = 2; 348 int SEND_URL_AND_CONTENT = 3; 349 int NUM_ENTRIES = 4; 350 } 351 352 /** 353 * Key used in maps from {state, reason} to state entry (exit) logging code. 354 */ 355 static class StateChangeKey { 356 final @PanelState int mState; 357 final @StateChangeReason int mReason; 358 final int mHashCode; 359 StateChangeKey(@anelState int state, @StateChangeReason int reason)360 StateChangeKey(@PanelState int state, @StateChangeReason int reason) { 361 mState = state; 362 mReason = reason; 363 mHashCode = 31 * state + reason; 364 } 365 366 @Override equals(Object obj)367 public boolean equals(Object obj) { 368 if (!(obj instanceof StateChangeKey)) return false; 369 if (obj == this) return true; 370 StateChangeKey other = (StateChangeKey) obj; 371 return mState == other.mState && mReason == other.mReason; 372 } 373 374 @Override hashCode()375 public int hashCode() { 376 return mHashCode; 377 } 378 } 379 380 // TODO(donnd): switch from using Maps to some method that does not require creation of a key. 381 382 // Entry code map: first entry into CLOSED. 383 private static final Map<StateChangeKey, Integer> ENTER_CLOSED_STATE_CHANGE_CODES; 384 static { 385 Map<StateChangeKey, Integer> codes = new HashMap<StateChangeKey, Integer>(); codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.BACK_PRESS), EnterClosedFrom.PEEKED_BACK_PRESS)386 codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.BACK_PRESS), 387 EnterClosedFrom.PEEKED_BACK_PRESS); codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.BASE_PAGE_SCROLL), EnterClosedFrom.PEEKED_BASE_PAGE_SCROLL)388 codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.BASE_PAGE_SCROLL), 389 EnterClosedFrom.PEEKED_BASE_PAGE_SCROLL); codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.TEXT_SELECT_TAP), EnterClosedFrom.PEEKED_TEXT_SELECT_TAP)390 codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.TEXT_SELECT_TAP), 391 EnterClosedFrom.PEEKED_TEXT_SELECT_TAP); codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.BACK_PRESS), EnterClosedFrom.EXPANDED_BACK_PRESS)392 codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.BACK_PRESS), 393 EnterClosedFrom.EXPANDED_BACK_PRESS); codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.BASE_PAGE_TAP), EnterClosedFrom.EXPANDED_BASE_PAGE_TAP)394 codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.BASE_PAGE_TAP), 395 EnterClosedFrom.EXPANDED_BASE_PAGE_TAP); codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.FLING), EnterClosedFrom.EXPANDED_FLING)396 codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.FLING), 397 EnterClosedFrom.EXPANDED_FLING); codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.BACK_PRESS), EnterClosedFrom.MAXIMIZED_BACK_PRESS)398 codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.BACK_PRESS), 399 EnterClosedFrom.MAXIMIZED_BACK_PRESS); codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.FLING), EnterClosedFrom.MAXIMIZED_FLING)400 codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.FLING), 401 EnterClosedFrom.MAXIMIZED_FLING); codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.TAB_PROMOTION), EnterClosedFrom.MAXIMIZED_TAB_PROMOTION)402 codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.TAB_PROMOTION), 403 EnterClosedFrom.MAXIMIZED_TAB_PROMOTION); codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.SERP_NAVIGATION), EnterClosedFrom.MAXIMIZED_SERP_NAVIGATION)404 codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.SERP_NAVIGATION), 405 EnterClosedFrom.MAXIMIZED_SERP_NAVIGATION); 406 ENTER_CLOSED_STATE_CHANGE_CODES = Collections.unmodifiableMap(codes); 407 } 408 409 // Entry code map: first entry into PEEKED. 410 private static final Map<StateChangeKey, Integer> ENTER_PEEKED_STATE_CHANGE_CODES; 411 static { 412 Map<StateChangeKey, Integer> codes = new HashMap<StateChangeKey, Integer>(); 413 // Note: we don't distinguish entering PEEKED from UNDEFINED / CLOSED. codes.put(new StateChangeKey(PanelState.UNDEFINED, StateChangeReason.TEXT_SELECT_TAP), EnterPeekedFrom.CLOSED_TEXT_SELECT_TAP)414 codes.put(new StateChangeKey(PanelState.UNDEFINED, StateChangeReason.TEXT_SELECT_TAP), 415 EnterPeekedFrom.CLOSED_TEXT_SELECT_TAP); codes.put( new StateChangeKey(PanelState.UNDEFINED, StateChangeReason.TEXT_SELECT_LONG_PRESS), EnterPeekedFrom.CLOSED_EXT_SELECT_LONG_PRESS)416 codes.put( 417 new StateChangeKey(PanelState.UNDEFINED, StateChangeReason.TEXT_SELECT_LONG_PRESS), 418 EnterPeekedFrom.CLOSED_EXT_SELECT_LONG_PRESS); codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.TEXT_SELECT_TAP), EnterPeekedFrom.CLOSED_TEXT_SELECT_TAP)419 codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.TEXT_SELECT_TAP), 420 EnterPeekedFrom.CLOSED_TEXT_SELECT_TAP); codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.TEXT_SELECT_LONG_PRESS), EnterPeekedFrom.CLOSED_EXT_SELECT_LONG_PRESS)421 codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.TEXT_SELECT_LONG_PRESS), 422 EnterPeekedFrom.CLOSED_EXT_SELECT_LONG_PRESS); codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.TEXT_SELECT_TAP), EnterPeekedFrom.PEEKED_TEXT_SELECT_TAP)423 codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.TEXT_SELECT_TAP), 424 EnterPeekedFrom.PEEKED_TEXT_SELECT_TAP); codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.TEXT_SELECT_LONG_PRESS), EnterPeekedFrom.PEEKED_TEXT_SELECT_LONG_PRESS)425 codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.TEXT_SELECT_LONG_PRESS), 426 EnterPeekedFrom.PEEKED_TEXT_SELECT_LONG_PRESS); codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.SEARCH_BAR_TAP), EnterPeekedFrom.EXPANDED_SEARCH_BAR_TAP)427 codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.SEARCH_BAR_TAP), 428 EnterPeekedFrom.EXPANDED_SEARCH_BAR_TAP); codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.SWIPE), EnterPeekedFrom.EXPANDED_SWIPE)429 codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.SWIPE), 430 EnterPeekedFrom.EXPANDED_SWIPE); codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.FLING), EnterPeekedFrom.EXPANDED_FLING)431 codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.FLING), 432 EnterPeekedFrom.EXPANDED_FLING); codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.SWIPE), EnterPeekedFrom.MAXIMIZED_SWIPE)433 codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.SWIPE), 434 EnterPeekedFrom.MAXIMIZED_SWIPE); codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.FLING), EnterPeekedFrom.MAXIMIZED_FLING)435 codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.FLING), 436 EnterPeekedFrom.MAXIMIZED_FLING); 437 ENTER_PEEKED_STATE_CHANGE_CODES = Collections.unmodifiableMap(codes); 438 } 439 440 // Entry code map: first entry into EXPANDED. 441 private static final Map<StateChangeKey, Integer> ENTER_EXPANDED_STATE_CHANGE_CODES; 442 static { 443 Map<StateChangeKey, Integer> codes = new HashMap<StateChangeKey, Integer>(); codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.SEARCH_BAR_TAP), EnterExpandedFrom.PEEKED_SEARCH_BAR_TAP)444 codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.SEARCH_BAR_TAP), 445 EnterExpandedFrom.PEEKED_SEARCH_BAR_TAP); codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.SWIPE), EnterExpandedFrom.PEEKED_SWIPE)446 codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.SWIPE), 447 EnterExpandedFrom.PEEKED_SWIPE); codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.FLING), EnterExpandedFrom.PEEKED_FLING)448 codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.FLING), 449 EnterExpandedFrom.PEEKED_FLING); codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.SWIPE), EnterExpandedFrom.MAXIMIZED_SWIPE)450 codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.SWIPE), 451 EnterExpandedFrom.MAXIMIZED_SWIPE); codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.FLING), EnterExpandedFrom.MAXIMIZED_FLING)452 codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.FLING), 453 EnterExpandedFrom.MAXIMIZED_FLING); 454 ENTER_EXPANDED_STATE_CHANGE_CODES = Collections.unmodifiableMap(codes); 455 } 456 457 // Entry code map: first entry into MAXIMIZED. 458 private static final Map<StateChangeKey, Integer> ENTER_MAXIMIZED_STATE_CHANGE_CODES; 459 static { 460 Map<StateChangeKey, Integer> codes = new HashMap<StateChangeKey, Integer>(); codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.SWIPE), EnterMaximizedFrom.PEEKED_SWIPE)461 codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.SWIPE), 462 EnterMaximizedFrom.PEEKED_SWIPE); codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.FLING), EnterMaximizedFrom.PEEKED_FLING)463 codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.FLING), 464 EnterMaximizedFrom.PEEKED_FLING); codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.SWIPE), EnterMaximizedFrom.EXPANDED_SWIPE)465 codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.SWIPE), 466 EnterMaximizedFrom.EXPANDED_SWIPE); codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.FLING), EnterMaximizedFrom.EXPANDED_FLING)467 codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.FLING), 468 EnterMaximizedFrom.EXPANDED_FLING); codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.SERP_NAVIGATION), EnterMaximizedFrom.EXPANDED_SERP_NAVIGATION)469 codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.SERP_NAVIGATION), 470 EnterMaximizedFrom.EXPANDED_SERP_NAVIGATION); 471 ENTER_MAXIMIZED_STATE_CHANGE_CODES = Collections.unmodifiableMap(codes); 472 } 473 474 // Exit code map: first exit from CLOSED. 475 private static final Map<StateChangeKey, Integer> EXIT_CLOSED_TO_STATE_CHANGE_CODES; 476 static { 477 Map<StateChangeKey, Integer> codes = new HashMap<StateChangeKey, Integer>(); codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.TEXT_SELECT_TAP), ExitClosedTo.PEEKED_TEXT_SELECT_TAP)478 codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.TEXT_SELECT_TAP), 479 ExitClosedTo.PEEKED_TEXT_SELECT_TAP); codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.TEXT_SELECT_LONG_PRESS), ExitClosedTo.PEEKED_TEXT_SELECT_LONG_PRESS)480 codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.TEXT_SELECT_LONG_PRESS), 481 ExitClosedTo.PEEKED_TEXT_SELECT_LONG_PRESS); 482 EXIT_CLOSED_TO_STATE_CHANGE_CODES = Collections.unmodifiableMap(codes); 483 } 484 485 // Exit code map: first exit from PEEKED. 486 private static final Map<StateChangeKey, Integer> EXIT_PEEKED_TO_STATE_CHANGE_CODES; 487 static { 488 Map<StateChangeKey, Integer> codes = new HashMap<StateChangeKey, Integer>(); codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.BACK_PRESS), ExitPeekedTo.CLOSED_BACK_PRESS)489 codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.BACK_PRESS), 490 ExitPeekedTo.CLOSED_BACK_PRESS); codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.BASE_PAGE_SCROLL), ExitPeekedTo.CLOSED_BASE_PAGE_SCROLL)491 codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.BASE_PAGE_SCROLL), 492 ExitPeekedTo.CLOSED_BASE_PAGE_SCROLL); codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.BASE_PAGE_TAP), ExitPeekedTo.CLOSED_TEXT_SELECT_TAP)493 codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.BASE_PAGE_TAP), 494 ExitPeekedTo.CLOSED_TEXT_SELECT_TAP); codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.TEXT_SELECT_TAP), ExitPeekedTo.PEEKED_TEXT_SELECT_TAP)495 codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.TEXT_SELECT_TAP), 496 ExitPeekedTo.PEEKED_TEXT_SELECT_TAP); codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.TEXT_SELECT_LONG_PRESS), ExitPeekedTo.PEEKED_TEXT_SELECT_LONG_PRESS)497 codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.TEXT_SELECT_LONG_PRESS), 498 ExitPeekedTo.PEEKED_TEXT_SELECT_LONG_PRESS); codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.SEARCH_BAR_TAP), ExitPeekedTo.EXPANDED_SEARCH_BAR_TAP)499 codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.SEARCH_BAR_TAP), 500 ExitPeekedTo.EXPANDED_SEARCH_BAR_TAP); codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.SWIPE), ExitPeekedTo.EXPANDED_SWIPE)501 codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.SWIPE), 502 ExitPeekedTo.EXPANDED_SWIPE); codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.FLING), ExitPeekedTo.EXPANDED_FLING)503 codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.FLING), 504 ExitPeekedTo.EXPANDED_FLING); codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.SWIPE), ExitPeekedTo.MAXIMIZED_SWIPE)505 codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.SWIPE), 506 ExitPeekedTo.MAXIMIZED_SWIPE); codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.FLING), ExitPeekedTo.MAXIMIZED_FLING)507 codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.FLING), 508 ExitPeekedTo.MAXIMIZED_FLING); 509 EXIT_PEEKED_TO_STATE_CHANGE_CODES = Collections.unmodifiableMap(codes); 510 } 511 512 // Exit code map: first exit from EXPANDED. 513 private static final Map<StateChangeKey, Integer> EXIT_EXPANDED_TO_STATE_CHANGE_CODES; 514 static { 515 Map<StateChangeKey, Integer> codes = new HashMap<StateChangeKey, Integer>(); codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.BACK_PRESS), ExitExpandedTo.CLOSED_BACK_PRESS)516 codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.BACK_PRESS), 517 ExitExpandedTo.CLOSED_BACK_PRESS); codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.BASE_PAGE_TAP), ExitExpandedTo.CLOSED_BASE_PAGE_TAP)518 codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.BASE_PAGE_TAP), 519 ExitExpandedTo.CLOSED_BASE_PAGE_TAP); codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.FLING), ExitExpandedTo.CLOSED_FLING)520 codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.FLING), 521 ExitExpandedTo.CLOSED_FLING); codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.SEARCH_BAR_TAP), ExitExpandedTo.PEEKED_SEARCH_BAR_TAP)522 codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.SEARCH_BAR_TAP), 523 ExitExpandedTo.PEEKED_SEARCH_BAR_TAP); codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.SWIPE), ExitExpandedTo.PEEKED_SWIPE)524 codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.SWIPE), 525 ExitExpandedTo.PEEKED_SWIPE); codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.FLING), ExitExpandedTo.PEEKED_FLING)526 codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.FLING), 527 ExitExpandedTo.PEEKED_FLING); codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.SWIPE), ExitExpandedTo.MAXIMIZED_SWIPE)528 codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.SWIPE), 529 ExitExpandedTo.MAXIMIZED_SWIPE); codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.FLING), ExitExpandedTo.MAXIMIZED_FLING)530 codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.FLING), 531 ExitExpandedTo.MAXIMIZED_FLING); codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.SERP_NAVIGATION), ExitExpandedTo.MAXIMIZED_SERP_NAVIGATION)532 codes.put(new StateChangeKey(PanelState.MAXIMIZED, StateChangeReason.SERP_NAVIGATION), 533 ExitExpandedTo.MAXIMIZED_SERP_NAVIGATION); 534 EXIT_EXPANDED_TO_STATE_CHANGE_CODES = Collections.unmodifiableMap(codes); 535 } 536 537 // Exit code map: first exit from MAXIMIZED. 538 private static final Map<StateChangeKey, Integer> EXIT_MAXIMIZED_TO_STATE_CHANGE_CODES; 539 static { 540 Map<StateChangeKey, Integer> codes = new HashMap<StateChangeKey, Integer>(); codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.BACK_PRESS), ExitMaximizedTo.CLOSED_BACK_PRESS)541 codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.BACK_PRESS), 542 ExitMaximizedTo.CLOSED_BACK_PRESS); codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.FLING), ExitMaximizedTo.CLOSED_FLING)543 codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.FLING), 544 ExitMaximizedTo.CLOSED_FLING); codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.TAB_PROMOTION), ExitMaximizedTo.CLOSED_TAB_PROMOTION)545 codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.TAB_PROMOTION), 546 ExitMaximizedTo.CLOSED_TAB_PROMOTION); codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.SERP_NAVIGATION), ExitMaximizedTo.CLOSED_SERP_NAVIGATION)547 codes.put(new StateChangeKey(PanelState.CLOSED, StateChangeReason.SERP_NAVIGATION), 548 ExitMaximizedTo.CLOSED_SERP_NAVIGATION); codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.SWIPE), ExitMaximizedTo.PEEKED_SWIPE)549 codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.SWIPE), 550 ExitMaximizedTo.PEEKED_SWIPE); codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.FLING), ExitMaximizedTo.PEEKED_FLING)551 codes.put(new StateChangeKey(PanelState.PEEKED, StateChangeReason.FLING), 552 ExitMaximizedTo.PEEKED_FLING); codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.SWIPE), ExitMaximizedTo.EXPANDED_SWIPE)553 codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.SWIPE), 554 ExitMaximizedTo.EXPANDED_SWIPE); codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.FLING), ExitMaximizedTo.EXPANDED_FLING)555 codes.put(new StateChangeKey(PanelState.EXPANDED, StateChangeReason.FLING), 556 ExitMaximizedTo.EXPANDED_FLING); 557 EXIT_MAXIMIZED_TO_STATE_CHANGE_CODES = Collections.unmodifiableMap(codes); 558 } 559 560 // "Seen by gesture" code map: logged on first exit from expanded panel, or promo, 561 // broken down by gesture. 562 private static final Map<Pair<Boolean, Boolean>, Integer> SEEN_BY_GESTURE_CODES; 563 static { 564 final boolean unseen = false; 565 final boolean seen = true; 566 Map<Pair<Boolean, Boolean>, Integer> codes = new HashMap<Pair<Boolean, Boolean>, Integer>(); codes.put(new Pair<Boolean, Boolean>(seen, TAP), ResultsByGesture.SEEN_FROM_TAP)567 codes.put(new Pair<Boolean, Boolean>(seen, TAP), ResultsByGesture.SEEN_FROM_TAP); codes.put(new Pair<Boolean, Boolean>(unseen, TAP), ResultsByGesture.NOT_SEEN_FROM_TAP)568 codes.put(new Pair<Boolean, Boolean>(unseen, TAP), ResultsByGesture.NOT_SEEN_FROM_TAP); codes.put(new Pair<Boolean, Boolean>(seen, LONG_PRESS), ResultsByGesture.SEEN_FROM_LONG_PRESS)569 codes.put(new Pair<Boolean, Boolean>(seen, LONG_PRESS), 570 ResultsByGesture.SEEN_FROM_LONG_PRESS); codes.put(new Pair<Boolean, Boolean>(unseen, LONG_PRESS), ResultsByGesture.NOT_SEEN_FROM_LONG_PRESS)571 codes.put(new Pair<Boolean, Boolean>(unseen, LONG_PRESS), 572 ResultsByGesture.NOT_SEEN_FROM_LONG_PRESS); 573 SEEN_BY_GESTURE_CODES = Collections.unmodifiableMap(codes); 574 } 575 576 // "Promo outcome by gesture" code map: logged on exit from promo, broken down by gesture. 577 private static final Map<Pair<Integer, Boolean>, Integer> PROMO_BY_GESTURE_CODES; 578 static { 579 Map<Pair<Integer, Boolean>, Integer> codes = 580 new HashMap<Pair<Integer, Boolean>, Integer>(); codes.put(new Pair<Integer, Boolean>(Preference.ENABLED, TAP), Promo.ENABLED_FROM_TAP)581 codes.put(new Pair<Integer, Boolean>(Preference.ENABLED, TAP), Promo.ENABLED_FROM_TAP); codes.put(new Pair<Integer, Boolean>(Preference.DISABLED, TAP), Promo.DISABLED_FROM_TAP)582 codes.put(new Pair<Integer, Boolean>(Preference.DISABLED, TAP), Promo.DISABLED_FROM_TAP); codes.put(new Pair<Integer, Boolean>(Preference.UNINITIALIZED, TAP), Promo.UNDECIDED_FROM_TAP)583 codes.put(new Pair<Integer, Boolean>(Preference.UNINITIALIZED, TAP), 584 Promo.UNDECIDED_FROM_TAP); codes.put(new Pair<Integer, Boolean>(Preference.ENABLED, LONG_PRESS), Promo.ENABLED_FROM_LONG_PRESS)585 codes.put(new Pair<Integer, Boolean>(Preference.ENABLED, LONG_PRESS), 586 Promo.ENABLED_FROM_LONG_PRESS); codes.put(new Pair<Integer, Boolean>(Preference.DISABLED, LONG_PRESS), Promo.DISABLED_FROM_LONG_PRESS)587 codes.put(new Pair<Integer, Boolean>(Preference.DISABLED, LONG_PRESS), 588 Promo.DISABLED_FROM_LONG_PRESS); codes.put(new Pair<Integer, Boolean>(Preference.UNINITIALIZED, LONG_PRESS), Promo.UNDECIDED_FROM_LONG_PRESS)589 codes.put(new Pair<Integer, Boolean>(Preference.UNINITIALIZED, LONG_PRESS), 590 Promo.UNDECIDED_FROM_LONG_PRESS); 591 PROMO_BY_GESTURE_CODES = Collections.unmodifiableMap(codes); 592 } 593 594 /** 595 * Logs the state of the Contextual Search preference. This function should be called if the 596 * Contextual Search feature is active, and will track the different preference settings 597 * (disabled, enabled or uninitialized). Calling more than once is fine. 598 */ logPreferenceState()599 public static void logPreferenceState() { 600 RecordHistogram.recordEnumeratedHistogram("Search.ContextualSearchPreferenceState", 601 getPreferenceValue(), Preference.NUM_ENTRIES); 602 } 603 604 /** 605 * Logs the given number of promo taps remaining. Should be called only for users that 606 * are still undecided. 607 * @param promoTapsRemaining The number of taps remaining (should not be negative). 608 */ logPromoTapsRemaining(int promoTapsRemaining)609 public static void logPromoTapsRemaining(int promoTapsRemaining) { 610 if (promoTapsRemaining >= 0) { 611 RecordHistogram.recordCountHistogram("Search.ContextualSearchPromoTapsRemaining", 612 promoTapsRemaining); 613 } 614 } 615 616 /** 617 * Logs the historic number of times that a Tap gesture triggered the peeking promo 618 * for users that have never opened the panel. This should be called periodically for 619 * undecided users only. 620 * @param promoTaps The historic number of taps that have caused the peeking bar for the promo, 621 * for users that have never opened the panel. 622 */ logPromoTapsForNeverOpened(int promoTaps)623 public static void logPromoTapsForNeverOpened(int promoTaps) { 624 RecordHistogram.recordCountHistogram("Search.ContextualSearchPromoTapsForNeverOpened", 625 promoTaps); 626 } 627 628 /** 629 * Logs the historic number of times that a Tap gesture triggered the peeking promo before 630 * the user ever opened the panel. This should be called periodically for all users. 631 * @param promoTaps The historic number of taps that have caused the peeking bar for the promo 632 * before the first open of the panel, for all users that have ever opened the panel. 633 */ logPromoTapsBeforeFirstOpen(int promoTaps)634 public static void logPromoTapsBeforeFirstOpen(int promoTaps) { 635 RecordHistogram.recordCountHistogram("Search.ContextualSearchPromoTapsBeforeFirstOpen", 636 promoTaps); 637 } 638 639 /** 640 * Records the total count of times the promo panel has *ever* been opened. This should only 641 * be called when the user is still undecided. 642 * @param count The total historic count of times the panel has ever been opened for the 643 * current user. 644 */ logPromoOpenCount(int count)645 public static void logPromoOpenCount(int count) { 646 RecordHistogram.recordCountHistogram("Search.ContextualSearchPromoOpenCount", count); 647 } 648 649 /** 650 * Logs the number of taps that have been counted since the user last opened the panel, for 651 * undecided users. 652 * @param tapsSinceOpen The number of taps to log. 653 */ logTapsSinceOpenForUndecided(int tapsSinceOpen)654 public static void logTapsSinceOpenForUndecided(int tapsSinceOpen) { 655 RecordHistogram.recordCountHistogram("Search.ContextualSearchTapsSinceOpenUndecided", 656 tapsSinceOpen); 657 } 658 659 /** 660 * Logs the number of taps that have been counted since the user last opened the panel, for 661 * decided users. 662 * @param tapsSinceOpen The number of taps to log. 663 */ logTapsSinceOpenForDecided(int tapsSinceOpen)664 public static void logTapsSinceOpenForDecided(int tapsSinceOpen) { 665 RecordHistogram.recordCountHistogram("Search.ContextualSearchTapsSinceOpenDecided", 666 tapsSinceOpen); 667 } 668 669 /** 670 * Logs whether the Search Term was single or multiword. 671 * @param isSingleWord Whether the resolved search term is a single word or not. 672 */ logSearchTermResolvedWords(boolean isSingleWord)673 public static void logSearchTermResolvedWords(boolean isSingleWord) { 674 RecordHistogram.recordEnumeratedHistogram("Search.ContextualSearchResolvedTermWords", 675 isSingleWord ? ResolvedGranularity.SINGLE_WORD : ResolvedGranularity.MULTI_WORD, 676 ResolvedGranularity.NUM_ENTRIES); 677 } 678 679 /** 680 * Logs whether the base page was using the HTTP protocol or not. 681 * @param isHttpBasePage Whether the base page was using the HTTP protocol or not (should 682 * be false for HTTPS or other URIs). 683 */ logBasePageProtocol(boolean isHttpBasePage)684 public static void logBasePageProtocol(boolean isHttpBasePage) { 685 RecordHistogram.recordEnumeratedHistogram("Search.ContextualSearchBasePageProtocol", 686 isHttpBasePage ? Protocol.IS_HTTP : Protocol.NOT_HTTP, Protocol.NUM_ENTRIES); 687 } 688 689 /** 690 * Logs changes to the Contextual Search preference, aside from those resulting from the first 691 * run flow. 692 * @param enabled Whether the preference is being enabled or disabled. 693 */ logPreferenceChange(boolean enabled)694 public static void logPreferenceChange(boolean enabled) { 695 RecordHistogram.recordEnumeratedHistogram("Search.ContextualSearchPreferenceStateChange", 696 enabled ? Preference.ENABLED : Preference.DISABLED, Preference.NUM_ENTRIES); 697 } 698 699 /** 700 * Logs the outcome of the Promo. 701 * Logs multiple histograms; with and without the originating gesture. 702 * @param wasTap Whether the gesture that originally caused the panel to show was a Tap. 703 * @param wasMandatory Whether the Promo was mandatory. 704 */ logPromoOutcome(boolean wasTap, boolean wasMandatory)705 public static void logPromoOutcome(boolean wasTap, boolean wasMandatory) { 706 int preferenceCode = getPreferenceValue(); 707 RecordHistogram.recordEnumeratedHistogram("Search.ContextualSearchFirstRunFlowOutcome", 708 preferenceCode, Preference.NUM_ENTRIES); 709 710 int preferenceByGestureCode = getPromoByGestureStateCode(preferenceCode, wasTap); 711 if (wasMandatory) { 712 RecordHistogram.recordEnumeratedHistogram( 713 "Search.ContextualSearchMandatoryPromoOutcomeByGesture", 714 preferenceByGestureCode, Promo.NUM_ENTRIES); 715 } else { 716 RecordHistogram.recordEnumeratedHistogram( 717 "Search.ContextualSearchPromoOutcomeByGesture", preferenceByGestureCode, 718 Promo.NUM_ENTRIES); 719 } 720 } 721 722 /** 723 * Logs the duration of a Contextual Search panel being viewed by the user. 724 * @param wereResultsSeen Whether search results were seen. 725 * @param isChained Whether the Contextual Search ended with the start of another. 726 * @param durationMs The duration of the contextual search in milliseconds. 727 */ logDuration(boolean wereResultsSeen, boolean isChained, long durationMs)728 public static void logDuration(boolean wereResultsSeen, boolean isChained, long durationMs) { 729 if (wereResultsSeen) { 730 RecordHistogram.recordTimesHistogram("Search.ContextualSearchDurationSeen", durationMs); 731 } else if (isChained) { 732 RecordHistogram.recordTimesHistogram( 733 "Search.ContextualSearchDurationUnseenChained", durationMs); 734 } else { 735 RecordHistogram.recordTimesHistogram( 736 "Search.ContextualSearchDurationUnseen", durationMs); 737 } 738 } 739 740 /** 741 * Logs the duration from starting a search until the Search Term is resolved. 742 * @param durationMs The duration to record. 743 */ logSearchTermResolutionDuration(long durationMs)744 public static void logSearchTermResolutionDuration(long durationMs) { 745 RecordHistogram.recordMediumTimesHistogram( 746 "Search.ContextualSearchResolutionDuration", durationMs); 747 } 748 749 /** 750 * Logs the duration from starting a prefetched search until the panel navigates to the results 751 * and they start becoming viewable. Should be called only for searches that are prefetched. 752 * @param durationMs The duration to record. 753 * @param didResolve Whether a Search Term resolution was required as part of the loading. 754 */ logPrefetchedSearchNavigatedDuration(long durationMs, boolean didResolve)755 public static void logPrefetchedSearchNavigatedDuration(long durationMs, boolean didResolve) { 756 String histogramName = didResolve ? "Search.ContextualSearchResolvedSearchDuration" 757 : "Search.ContextualSearchLiteralSearchDuration"; 758 RecordHistogram.recordMediumTimesHistogram(histogramName, durationMs); 759 } 760 761 /** 762 * Logs the duration from opening the panel beyond peek until the panel is closed. 763 * @param durationMs The duration to record. 764 */ logPanelOpenDuration(long durationMs)765 public static void logPanelOpenDuration(long durationMs) { 766 RecordHistogram.recordMediumTimesHistogram( 767 "Search.ContextualSearchPanelOpenDuration", durationMs); 768 } 769 770 /** 771 * When Contextual Search panel is opened, logs whether In-Product Help for opening the panel 772 * was ever shown. 773 * @param wasIPHShown Whether In-Product help was shown. 774 */ logPanelOpenedIPH(boolean wasIPHShown)775 public static void logPanelOpenedIPH(boolean wasIPHShown) { 776 RecordHistogram.recordBooleanHistogram( 777 "Search.ContextualSearchPanelOpenedIPHShown", wasIPHShown); 778 } 779 780 /** 781 * When Contextual Search panel is opened, logs whether In-Product Help for Contextual Search 782 * was ever shown. 783 * @param wasIPHShown Whether In-Product help was shown. 784 */ logContextualSearchIPH(boolean wasIPHShown)785 public static void logContextualSearchIPH(boolean wasIPHShown) { 786 RecordHistogram.recordBooleanHistogram("Search.ContextualSearchIPHShown", wasIPHShown); 787 } 788 789 /** 790 * When Contextual Search is triggered by tapping, logs whether In-Product Help for tapping was 791 * ever shown. 792 * @param wasIPHShown Whether In-Product help was shown. 793 */ logTapIPH(boolean wasIPHShown)794 public static void logTapIPH(boolean wasIPHShown) { 795 RecordHistogram.recordBooleanHistogram("Search.ContextualSearchTapIPHShown", wasIPHShown); 796 } 797 798 /** 799 * Logs whether we have ever shown an In-Product Help for Translations suggesting that the user 800 * Opt-in. 801 * @param wasIPHShown Whether In-Product help was shown. 802 */ logTranslationsOptInIPHShown(boolean wasIPHShown)803 public static void logTranslationsOptInIPHShown(boolean wasIPHShown) { 804 RecordHistogram.recordBooleanHistogram( 805 "Search.ContextualSearch.TranslationsOptInIPHShown", wasIPHShown); 806 } 807 808 /** 809 * Logs whether the user actually did opt-in after seeing the In-Product Help for Translations 810 * suggesting that the user should Opt-in. 811 * @param didOptIn Whether the user did opt-in. 812 */ logTranslationsOptInIPHWorked(boolean didOptIn)813 public static void logTranslationsOptInIPHWorked(boolean didOptIn) { 814 RecordHistogram.recordBooleanHistogram( 815 "Search.ContextualSearch.TranslationsOptInIPHWorked", didOptIn); 816 } 817 818 /** 819 * Logs a user action for the duration of viewing the panel that describes the amount of time 820 * the user viewed the bar and panel overall. 821 * @param durationMs The duration to record. 822 */ logPanelViewDurationAction(long durationMs)823 public static void logPanelViewDurationAction(long durationMs) { 824 if (durationMs < DateUtils.SECOND_IN_MILLIS) { 825 RecordUserAction.record("ContextualSearch.ViewLessThanOneSecond"); 826 } else if (durationMs < DateUtils.SECOND_IN_MILLIS * 3) { 827 RecordUserAction.record("ContextualSearch.ViewOneToThreeSeconds"); 828 } else if (durationMs < DateUtils.SECOND_IN_MILLIS * 10) { 829 RecordUserAction.record("ContextualSearch.ViewThreeToTenSeconds"); 830 } else { 831 RecordUserAction.record("ContextualSearch.ViewMoreThanTenSeconds"); 832 } 833 } 834 835 /** 836 * Logs whether the promo was seen. 837 * Logs multiple histograms, with and without the original triggering gesture. 838 * @param wasPanelSeen Whether the panel was seen. 839 * @param wasTap Whether the gesture that originally caused the panel to show was a Tap. 840 */ logPromoSeen(boolean wasPanelSeen, boolean wasTap)841 public static void logPromoSeen(boolean wasPanelSeen, boolean wasTap) { 842 RecordHistogram.recordEnumeratedHistogram("Search.ContextualSearchFirstRunPanelSeen", 843 wasPanelSeen ? Results.SEEN : Results.NOT_SEEN, Results.NUM_ENTRIES); 844 logHistogramByGesture(wasPanelSeen, wasTap, "Search.ContextualSearchPromoSeenByGesture"); 845 } 846 847 /** 848 * Logs whether search results were seen. 849 * Logs multiple histograms; with and without the original triggering gesture. 850 * @param wasPanelSeen Whether the panel was seen. 851 * @param wasTap Whether the gesture that originally caused the panel to show was a Tap. 852 */ logResultsSeen(boolean wasPanelSeen, boolean wasTap)853 public static void logResultsSeen(boolean wasPanelSeen, boolean wasTap) { 854 RecordHistogram.recordEnumeratedHistogram("Search.ContextualSearchResultsSeen", 855 wasPanelSeen ? Results.SEEN : Results.NOT_SEEN, Results.NUM_ENTRIES); 856 logHistogramByGesture(wasPanelSeen, wasTap, "Search.ContextualSearchResultsSeenByGesture"); 857 } 858 859 /** 860 * Logs whether search results were seen for a Tap gesture, for all users and sync-enabled 861 * users. For sync-enabled users we log to a separate histogram for that sub-population in order 862 * to help validate the Ranker Tap Suppression model results (since they are trained on UKM data 863 * which approximately reflects this sync-enabled population). 864 * @param wasPanelSeen Whether the panel was seen. 865 */ logTapResultsSeen(boolean wasPanelSeen)866 public static void logTapResultsSeen(boolean wasPanelSeen) { 867 RecordHistogram.recordBooleanHistogram( 868 "Search.ContextualSearch.Tap.ResultsSeen", wasPanelSeen); 869 if (AndroidSyncSettings.get().isSyncEnabled()) { 870 RecordHistogram.recordBooleanHistogram( 871 "Search.ContextualSearch.Tap.SyncEnabled.ResultsSeen", wasPanelSeen); 872 } 873 } 874 875 /** 876 * Logs whether search results were seen for all gestures. Recorded for all users. 877 * @param wasPanelSeen Whether the panel was seen. 878 */ logAllResultsSeen(boolean wasPanelSeen)879 public static void logAllResultsSeen(boolean wasPanelSeen) { 880 RecordHistogram.recordBooleanHistogram( 881 "Search.ContextualSearch.All.ResultsSeen", wasPanelSeen); 882 } 883 884 /** 885 * Logs the whether the panel was seen and the type of the trigger and if Bar nearly overlapped. 886 * If the panel was seen, logs the duration of the panel view into a BarOverlap or BarNoOverlap 887 * duration histogram. 888 * @param wasPanelSeen Whether the panel was seen. 889 * @param wasTap Whether the gesture was a Tap or not. 890 * @param wasBarOverlap Whether the trigger location overlapped the Bar area. 891 */ logBarOverlapResultsSeen( boolean wasPanelSeen, boolean wasTap, boolean wasBarOverlap)892 public static void logBarOverlapResultsSeen( 893 boolean wasPanelSeen, boolean wasTap, boolean wasBarOverlap) { 894 RecordHistogram.recordEnumeratedHistogram("Search.ContextualSearchBarOverlapSeen", 895 getBarOverlapEnum(wasBarOverlap, wasPanelSeen, wasTap), 896 BarOverlapResults.NUM_ENTRIES); 897 } 898 899 /** 900 * Logs the duration of the panel viewed in its Peeked state before being opened. 901 * @param wasBarOverlap Whether the trigger location overlapped the Bar area. 902 * @param panelPeekDurationMs The duration that the panel was peeking before being opened 903 * by the user. 904 */ logBarOverlapPeekDuration(boolean wasBarOverlap, long panelPeekDurationMs)905 public static void logBarOverlapPeekDuration(boolean wasBarOverlap, long panelPeekDurationMs) { 906 String histogram = wasBarOverlap ? "Search.ContextualSearchBarOverlap.PeekDuration" 907 : "Search.ContextualSearchBarNoOverlap.PeekDuration"; 908 RecordHistogram.recordMediumTimesHistogram(histogram, panelPeekDurationMs); 909 } 910 911 /** 912 * Log whether the UX was suppressed due to Bar overlap. 913 * @param wasSuppressed Whether showing the UX was suppressed. 914 */ logBarOverlapSuppression(boolean wasSuppressed)915 public static void logBarOverlapSuppression(boolean wasSuppressed) { 916 RecordHistogram.recordBooleanHistogram("Search.ContextualSearchBarOverlap", wasSuppressed); 917 } 918 919 /** 920 * Logs the length of the selection in two histograms, one when results were seen and one when 921 * results were not seen. 922 * @param wasPanelSeen Whether the panel was seen. 923 * @param selectionLength The length of the triggering selection. 924 */ logSelectionLengthResultsSeen(boolean wasPanelSeen, int selectionLength)925 public static void logSelectionLengthResultsSeen(boolean wasPanelSeen, int selectionLength) { 926 if (wasPanelSeen) { 927 RecordHistogram.recordSparseHistogram( 928 "Search.ContextualSearchSelectionLengthSeen", selectionLength); 929 } else { 930 RecordHistogram.recordSparseHistogram( 931 "Search.ContextualSearchSelectionLengthNotSeen", selectionLength); 932 } 933 } 934 935 /** 936 * Log whether the UX was suppressed due to the selection length. 937 * @param wasSuppressed Whether showing the UX was suppressed due to selection length. 938 */ logSelectionLengthSuppression(boolean wasSuppressed)939 public static void logSelectionLengthSuppression(boolean wasSuppressed) { 940 RecordHistogram.recordBooleanHistogram( 941 "Search.ContextualSearchSelectionLengthSuppression", wasSuppressed); 942 } 943 944 /** 945 * Logs the location of a Tap and whether the panel was seen and the type of the 946 * trigger. 947 * @param wasPanelSeen Whether the panel was seen. 948 * @param wasTap Whether the gesture was a Tap or not. 949 * @param triggerLocationDps The trigger location from the top of the screen. 950 */ logScreenTopTapLocation( boolean wasPanelSeen, boolean wasTap, int triggerLocationDps)951 public static void logScreenTopTapLocation( 952 boolean wasPanelSeen, boolean wasTap, int triggerLocationDps) { 953 // We only log Tap locations for the screen top. 954 if (!wasTap) return; 955 String histogram = wasPanelSeen ? "Search.ContextualSearchTopLocationSeen" 956 : "Search.ContextualSearchTopLocationNotSeen"; 957 int min = 1; 958 int max = 250; 959 int numBuckets = 50; 960 RecordHistogram.recordCustomCountHistogram( 961 histogram, triggerLocationDps, min, max, numBuckets); 962 } 963 964 /** 965 * Log whether the UX was suppressed due to a Tap too close to the screen top. 966 * @param wasSuppressed Whether showing the UX was suppressed. 967 */ logScreenTopTapSuppression(boolean wasSuppressed)968 public static void logScreenTopTapSuppression(boolean wasSuppressed) { 969 RecordHistogram.recordBooleanHistogram( 970 "Search.ContextualSearchScreenTopSuppressed", wasSuppressed); 971 } 972 973 /** 974 * Log whether results were seen due to a Tap that was allowed to override an ML suppression. 975 * @param wasSearchContentViewSeen If the panel was opened. 976 */ logSecondTapMlOverrideResultsSeen(boolean wasSearchContentViewSeen)977 static void logSecondTapMlOverrideResultsSeen(boolean wasSearchContentViewSeen) { 978 RecordHistogram.recordBooleanHistogram( 979 "Search.ContextualSearchSecondTapMlOverrideSeen", wasSearchContentViewSeen); 980 } 981 982 /** 983 * Logs whether results were seen based on the duration of the Tap, for both short and long 984 * durations. 985 * @param wasSearchContentViewSeen If the panel was opened. 986 * @param isTapShort Whether this tap was "short" in duration. 987 */ logTapDurationSeen(boolean wasSearchContentViewSeen, boolean isTapShort)988 public static void logTapDurationSeen(boolean wasSearchContentViewSeen, boolean isTapShort) { 989 if (isTapShort) { 990 RecordHistogram.recordEnumeratedHistogram("Search.ContextualSearchTapShortDurationSeen", 991 wasSearchContentViewSeen ? Results.SEEN : Results.NOT_SEEN, 992 Results.NUM_ENTRIES); 993 } else { 994 RecordHistogram.recordEnumeratedHistogram("Search.ContextualSearchTapLongDurationSeen", 995 wasSearchContentViewSeen ? Results.SEEN : Results.NOT_SEEN, 996 Results.NUM_ENTRIES); 997 } 998 } 999 1000 /** 1001 * Logs the duration of a Tap in ms into custom histograms to profile the duration of seen 1002 * and not seen taps. 1003 * @param wasPanelSeen Whether the panel was seen. 1004 * @param durationMs The duration of the tap gesture. 1005 */ logTapDuration(boolean wasPanelSeen, int durationMs)1006 public static void logTapDuration(boolean wasPanelSeen, int durationMs) { 1007 int min = 1; 1008 int max = 1000; 1009 int numBuckets = 100; 1010 1011 if (wasPanelSeen) { 1012 RecordHistogram.recordCustomCountHistogram( 1013 "Search.ContextualSearchTapDurationSeen", durationMs, min, max, numBuckets); 1014 } else { 1015 RecordHistogram.recordCustomCountHistogram( 1016 "Search.ContextualSearchTapDurationNotSeen", durationMs, min, max, numBuckets); 1017 } 1018 } 1019 1020 /** 1021 * Log whether results were seen due to a Tap on a short word. 1022 * @param wasSearchContentViewSeen If the panel was opened. 1023 * @param isTapOnShortWord Whether this tap was on a "short" word. 1024 */ logTapShortWordSeen( boolean wasSearchContentViewSeen, boolean isTapOnShortWord)1025 public static void logTapShortWordSeen( 1026 boolean wasSearchContentViewSeen, boolean isTapOnShortWord) { 1027 if (!isTapOnShortWord) return; 1028 1029 // We just record CTR of short words. 1030 RecordHistogram.recordEnumeratedHistogram("Search.ContextualSearchTapShortWordSeen", 1031 wasSearchContentViewSeen ? Results.SEEN : Results.NOT_SEEN, Results.NUM_ENTRIES); 1032 } 1033 1034 /** 1035 * Log whether results were seen due to a Tap on a long word. 1036 * @param wasSearchContentViewSeen If the panel was opened. 1037 * @param isTapOnLongWord Whether this tap was on a long word. 1038 */ logTapLongWordSeen( boolean wasSearchContentViewSeen, boolean isTapOnLongWord)1039 public static void logTapLongWordSeen( 1040 boolean wasSearchContentViewSeen, boolean isTapOnLongWord) { 1041 if (!isTapOnLongWord) return; 1042 1043 RecordHistogram.recordEnumeratedHistogram("Search.ContextualSearchTapLongWordSeen", 1044 wasSearchContentViewSeen ? Results.SEEN : Results.NOT_SEEN, Results.NUM_ENTRIES); 1045 } 1046 1047 /** 1048 * Log whether results were seen due to a Tap that was on the middle of a word. 1049 * @param wasSearchContentViewSeen If the panel was opened. 1050 * @param isTapOnWordMiddle Whether this tap was on the middle of a word. 1051 */ logTapOnWordMiddleSeen( boolean wasSearchContentViewSeen, boolean isTapOnWordMiddle)1052 public static void logTapOnWordMiddleSeen( 1053 boolean wasSearchContentViewSeen, boolean isTapOnWordMiddle) { 1054 if (!isTapOnWordMiddle) return; 1055 1056 // We just record CTR of words tapped in the "middle". 1057 RecordHistogram.recordEnumeratedHistogram("Search.ContextualSearchTapOnWordMiddleSeen", 1058 wasSearchContentViewSeen ? Results.SEEN : Results.NOT_SEEN, Results.NUM_ENTRIES); 1059 } 1060 1061 /** 1062 * Log whether results were seen due to a Tap on what we've recognized as a probable entity. 1063 * @param wasSearchContentViewSeen If the panel was opened. 1064 * @param isWordAnEntity Whether this tap was on a word that's an entity. 1065 */ logTapOnEntitySeen( boolean wasSearchContentViewSeen, boolean isWordAnEntity)1066 public static void logTapOnEntitySeen( 1067 boolean wasSearchContentViewSeen, boolean isWordAnEntity) { 1068 if (isWordAnEntity) { 1069 // We just record CTR of probable entity words. 1070 RecordHistogram.recordEnumeratedHistogram("Search.ContextualSearchEntitySeen", 1071 wasSearchContentViewSeen ? Results.SEEN : Results.NOT_SEEN, 1072 Results.NUM_ENTRIES); 1073 } 1074 } 1075 1076 /** 1077 * Logs whether results were seen and whether any tap suppression heuristics were satisfied. 1078 * @param wasSearchContentViewSeen If the panel was opened. 1079 * @param wasAnySuppressionHeuristicSatisfied Whether any of the implemented suppression 1080 * heuristics were satisfied. 1081 */ logAnyTapSuppressionHeuristicSatisfied(boolean wasSearchContentViewSeen, boolean wasAnySuppressionHeuristicSatisfied)1082 public static void logAnyTapSuppressionHeuristicSatisfied(boolean wasSearchContentViewSeen, 1083 boolean wasAnySuppressionHeuristicSatisfied) { 1084 int code; 1085 if (wasAnySuppressionHeuristicSatisfied) { 1086 code = wasSearchContentViewSeen 1087 ? ResultsBySuppression.SEEN_SUPPRESSION_HEURSTIC_SATISFIED 1088 : ResultsBySuppression.NOT_SEEN_SUPPRESSION_HEURSTIC_SATISFIED; 1089 } else { 1090 code = wasSearchContentViewSeen 1091 ? ResultsBySuppression.SEEN_SUPPRESSION_HEURSTIC_NOT_SATISFIED 1092 : ResultsBySuppression.NOT_SEEN_SUPPRESSION_HEURSTIC_NOT_SATISFIED; 1093 } 1094 1095 RecordHistogram.recordEnumeratedHistogram( 1096 "Search.ContextualSearchTapSuppressionSeen.AnyHeuristicSatisfied", code, 1097 ResultsBySuppression.NUM_ENTRIES); 1098 } 1099 1100 /** 1101 * Logs whether a selection is valid. 1102 * @param isSelectionValid Whether the selection is valid. 1103 */ logSelectionIsValid(boolean isSelectionValid)1104 public static void logSelectionIsValid(boolean isSelectionValid) { 1105 RecordHistogram.recordEnumeratedHistogram("Search.ContextualSearchSelectionValid", 1106 isSelectionValid ? Selection.VALID : Selection.INVALID, Selection.NUM_ENTRIES); 1107 } 1108 1109 /** 1110 * Logs whether a normal priority search request failed. 1111 * @param isFailure Whether the request failed. 1112 */ logNormalPrioritySearchRequestOutcome(boolean isFailure)1113 public static void logNormalPrioritySearchRequestOutcome(boolean isFailure) { 1114 RecordHistogram.recordEnumeratedHistogram( 1115 "Search.ContextualSearchNormalPrioritySearchRequestStatus", 1116 isFailure ? Request.FAILED : Request.NOT_FAILED, Request.NUM_ENTRIES); 1117 } 1118 1119 /** 1120 * Logs whether a low priority search request failed. 1121 * @param isFailure Whether the request failed. 1122 */ logLowPrioritySearchRequestOutcome(boolean isFailure)1123 public static void logLowPrioritySearchRequestOutcome(boolean isFailure) { 1124 RecordHistogram.recordEnumeratedHistogram( 1125 "Search.ContextualSearchLowPrioritySearchRequestStatus", 1126 isFailure ? Request.FAILED : Request.NOT_FAILED, Request.NUM_ENTRIES); 1127 } 1128 1129 /** 1130 * Logs whether a fallback search request failed. 1131 * @param isFailure Whether the request failed. 1132 */ logFallbackSearchRequestOutcome(boolean isFailure)1133 public static void logFallbackSearchRequestOutcome(boolean isFailure) { 1134 RecordHistogram.recordEnumeratedHistogram( 1135 "Search.ContextualSearchFallbackSearchRequestStatus", 1136 isFailure ? Request.FAILED : Request.NOT_FAILED, Request.NUM_ENTRIES); 1137 } 1138 1139 /** 1140 * Log whether the UX was suppressed by a recent scroll. 1141 * @param wasSuppressed Whether showing the UX was suppressed by a recent scroll. 1142 */ logRecentScrollSuppression(boolean wasSuppressed)1143 public static void logRecentScrollSuppression(boolean wasSuppressed) { 1144 RecordHistogram.recordBooleanHistogram( 1145 "Search.ContextualSearchRecentScrollSuppression", wasSuppressed); 1146 } 1147 1148 /** 1149 * Logs the duration between the panel being triggered due to a tap and the panel being 1150 * dismissed due to a scroll. 1151 * @param durationSincePanelTriggerMs The amount of time between the panel getting triggered and 1152 * the panel being dismissed due to a scroll. 1153 * @param wasSearchContentViewSeen If the panel was opened. 1154 */ logDurationBetweenTriggerAndScroll( long durationSincePanelTriggerMs, boolean wasSearchContentViewSeen)1155 public static void logDurationBetweenTriggerAndScroll( 1156 long durationSincePanelTriggerMs, boolean wasSearchContentViewSeen) { 1157 String histogram = wasSearchContentViewSeen 1158 ? "Search.ContextualSearchDurationBetweenTriggerAndScrollSeen" 1159 : "Search.ContextualSearchDurationBetweenTriggerAndScrollNotSeen"; 1160 if (durationSincePanelTriggerMs < 2000) { 1161 RecordHistogram.recordCustomCountHistogram( 1162 histogram, (int) durationSincePanelTriggerMs, 1, 2000, 200); 1163 } 1164 } 1165 1166 /** 1167 * Logs whether a Quick Answer caption was activated, and whether it was an answer (as opposed 1168 * to just being informative), and whether the panel was opened anyway. 1169 * Logged only for Tap events. 1170 * @param didActivate If the Quick Answer caption was shown. 1171 * @param didAnswer If the caption was considered an answer (reducing the need to open the 1172 * panel). 1173 * @param wasSearchContentViewSeen If the panel was opened. 1174 */ logQuickAnswerSeen( boolean wasSearchContentViewSeen, boolean didActivate, boolean didAnswer)1175 static void logQuickAnswerSeen( 1176 boolean wasSearchContentViewSeen, boolean didActivate, boolean didAnswer) { 1177 RecordHistogram.recordEnumeratedHistogram("Search.ContextualSearchQuickAnswerSeen", 1178 getQuickAnswerSeenValue(didActivate, didAnswer, wasSearchContentViewSeen), 1179 QuickAnswerSeen.NUM_ENTRIES); 1180 } 1181 1182 /** 1183 * Logs how a state was entered for the first time within a Contextual Search. 1184 * @param fromState The state to transition from. 1185 * @param toState The state to transition to. 1186 * @param reason The reason for the state transition. 1187 */ logFirstStateEntry( @anelState int fromState, @PanelState int toState, @StateChangeReason int reason)1188 public static void logFirstStateEntry( 1189 @PanelState int fromState, @PanelState int toState, @StateChangeReason int reason) { 1190 int code; 1191 switch (toState) { 1192 case PanelState.CLOSED: 1193 code = getStateChangeCode( 1194 fromState, reason, ENTER_CLOSED_STATE_CHANGE_CODES, EnterClosedFrom.OTHER); 1195 RecordHistogram.recordEnumeratedHistogram( 1196 "Search.ContextualSearchEnterClosed", code, EnterClosedFrom.NUM_ENTRIES); 1197 break; 1198 case PanelState.PEEKED: 1199 code = getStateChangeCode( 1200 fromState, reason, ENTER_PEEKED_STATE_CHANGE_CODES, EnterPeekedFrom.OTHER); 1201 RecordHistogram.recordEnumeratedHistogram( 1202 "Search.ContextualSearchEnterPeeked", code, EnterPeekedFrom.NUM_ENTRIES); 1203 break; 1204 case PanelState.EXPANDED: 1205 code = getStateChangeCode(fromState, reason, ENTER_EXPANDED_STATE_CHANGE_CODES, 1206 EnterExpandedFrom.OTHER); 1207 RecordHistogram.recordEnumeratedHistogram("Search.ContextualSearchEnterExpanded", 1208 code, EnterExpandedFrom.NUM_ENTRIES); 1209 break; 1210 case PanelState.MAXIMIZED: 1211 code = getStateChangeCode(fromState, reason, ENTER_MAXIMIZED_STATE_CHANGE_CODES, 1212 EnterMaximizedFrom.OTHER); 1213 RecordHistogram.recordEnumeratedHistogram("Search.ContextualSearchEnterMaximized", 1214 code, EnterMaximizedFrom.NUM_ENTRIES); 1215 break; 1216 default: 1217 break; 1218 } 1219 } 1220 1221 /** 1222 * Logs a user action for a change to the Panel state, which allows sequencing of actions. 1223 * @param toState The state to transition to. 1224 * @param reason The reason for the state transition. 1225 */ logPanelStateUserAction( @anelState int toState, @StateChangeReason int reason)1226 public static void logPanelStateUserAction( 1227 @PanelState int toState, @StateChangeReason int reason) { 1228 switch (toState) { 1229 case PanelState.CLOSED: 1230 if (reason == StateChangeReason.BACK_PRESS) { 1231 RecordUserAction.record("ContextualSearch.BackPressClose"); 1232 } else if (reason == StateChangeReason.CLOSE_BUTTON) { 1233 RecordUserAction.record("ContextualSearch.CloseButtonClose"); 1234 } else if (reason == StateChangeReason.SWIPE || reason == StateChangeReason.FLING) { 1235 RecordUserAction.record("ContextualSearch.SwipeOrFlingClose"); 1236 } else if (reason == StateChangeReason.TAB_PROMOTION) { 1237 RecordUserAction.record("ContextualSearch.TabPromotionClose"); 1238 } else if (reason == StateChangeReason.BASE_PAGE_TAP) { 1239 RecordUserAction.record("ContextualSearch.BasePageTapClose"); 1240 } else if (reason == StateChangeReason.BASE_PAGE_SCROLL) { 1241 RecordUserAction.record("ContextualSearch.BasePageScrollClose"); 1242 } else if (reason == StateChangeReason.SEARCH_BAR_TAP) { 1243 RecordUserAction.record("ContextualSearch.SearchBarTapClose"); 1244 } else if (reason == StateChangeReason.SERP_NAVIGATION) { 1245 RecordUserAction.record("ContextualSearch.NavigationClose"); 1246 } else { 1247 RecordUserAction.record("ContextualSearch.UncommonClose"); 1248 } 1249 break; 1250 case PanelState.PEEKED: 1251 if (reason == StateChangeReason.TEXT_SELECT_TAP) { 1252 RecordUserAction.record("ContextualSearch.TapPeek"); 1253 } else if (reason == StateChangeReason.SWIPE || reason == StateChangeReason.FLING) { 1254 RecordUserAction.record("ContextualSearch.SwipeOrFlingPeek"); 1255 } else if (reason == StateChangeReason.TEXT_SELECT_LONG_PRESS) { 1256 RecordUserAction.record("ContextualSearch.LongpressPeek"); 1257 } 1258 break; 1259 case PanelState.EXPANDED: 1260 if (reason == StateChangeReason.SWIPE || reason == StateChangeReason.FLING) { 1261 RecordUserAction.record("ContextualSearch.SwipeOrFlingExpand"); 1262 } else if (reason == StateChangeReason.SEARCH_BAR_TAP) { 1263 RecordUserAction.record("ContextualSearch.SearchBarTapExpand"); 1264 } 1265 break; 1266 case PanelState.MAXIMIZED: 1267 if (reason == StateChangeReason.SWIPE || reason == StateChangeReason.FLING) { 1268 RecordUserAction.record("ContextualSearch.SwipeOrFlingMaximize"); 1269 } else if (reason == StateChangeReason.SERP_NAVIGATION) { 1270 RecordUserAction.record("ContextualSearch.NavigationMaximize"); 1271 } 1272 break; 1273 default: 1274 break; 1275 } 1276 } 1277 1278 /** 1279 * Logs that the user established a new selection when Contextual Search is active. 1280 */ logSelectionEstablished()1281 public static void logSelectionEstablished() { 1282 RecordUserAction.record("ContextualSearch.SelectionEstablished"); 1283 } 1284 1285 /** 1286 * Logs that the user manually adjusted a selection when Contextual Search is active. 1287 * @param selection The new selection. 1288 */ logSelectionAdjusted(@ullable String selection)1289 public static void logSelectionAdjusted(@Nullable String selection) { 1290 if (TextUtils.isEmpty(selection)) return; 1291 1292 boolean isSingleWord = !CONTAINS_WHITESPACE_PATTERN.matcher(selection.trim()).find(); 1293 if (isSingleWord) { 1294 RecordUserAction.record("ContextualSearch.ManualRefineSingleWord"); 1295 } else { 1296 RecordUserAction.record("ContextualSearch.ManualRefineMultiWord"); 1297 } 1298 } 1299 1300 /** 1301 * Logs that the system automatically expanded the selection when a user triggered 1302 * Contextual Search on a multiword phrase that could be identified by the server. 1303 * @param fromTapGesture Whether the gesture that originally established the selection 1304 * was Tap. 1305 */ logSelectionExpanded(boolean fromTapGesture)1306 public static void logSelectionExpanded(boolean fromTapGesture) { 1307 RecordHistogram.recordBooleanHistogram( 1308 "Search.ContextualSearch.SelectionExpanded", fromTapGesture); 1309 } 1310 1311 /** 1312 * Logs that the system sent a server request to resolve the search term. 1313 * @param fromTapGesture Whether the gesture that originally established the selection 1314 * was Tap. 1315 */ logResolveRequested(boolean fromTapGesture)1316 public static void logResolveRequested(boolean fromTapGesture) { 1317 RecordHistogram.recordBooleanHistogram( 1318 "Search.ContextualSearch.ResolveRequested", fromTapGesture); 1319 } 1320 1321 /** 1322 * Logs that the system received a server response from a resolve request. 1323 * @param fromTapGesture Whether the gesture that originally established the selection 1324 * was Tap. 1325 */ logResolveReceived(boolean fromTapGesture)1326 public static void logResolveReceived(boolean fromTapGesture) { 1327 RecordHistogram.recordBooleanHistogram( 1328 "Search.ContextualSearch.ResolveReceived", fromTapGesture); 1329 } 1330 1331 /** 1332 * Logs that the user needs a translation of the selection. The user may or may not actually 1333 * see a translation - this only logs that it's needed. 1334 * @param fromTapGesture Whether the gesture that originally established the selection 1335 * was Tap. 1336 */ logTranslationNeeded(boolean fromTapGesture)1337 public static void logTranslationNeeded(boolean fromTapGesture) { 1338 RecordHistogram.recordBooleanHistogram( 1339 "Search.ContextualSearch.TranslationNeeded", fromTapGesture); 1340 } 1341 1342 /** 1343 * Logs how a state was exited for the first time within a Contextual Search. 1344 * @param fromState The state to transition from. 1345 * @param toState The state to transition to. 1346 * @param reason The reason for the state transition. 1347 */ logFirstStateExit( @anelState int fromState, @PanelState int toState, @StateChangeReason int reason)1348 public static void logFirstStateExit( 1349 @PanelState int fromState, @PanelState int toState, @StateChangeReason int reason) { 1350 int code; 1351 switch (fromState) { 1352 case PanelState.UNDEFINED: 1353 case PanelState.CLOSED: 1354 code = getStateChangeCode( 1355 toState, reason, EXIT_CLOSED_TO_STATE_CHANGE_CODES, ExitClosedTo.OTHER); 1356 RecordHistogram.recordEnumeratedHistogram( 1357 "Search.ContextualSearchExitClosed", code, ExitClosedTo.NUM_ENTRIES); 1358 break; 1359 case PanelState.PEEKED: 1360 code = getStateChangeCode( 1361 toState, reason, EXIT_PEEKED_TO_STATE_CHANGE_CODES, ExitPeekedTo.OTHER); 1362 RecordHistogram.recordEnumeratedHistogram( 1363 "Search.ContextualSearchExitPeeked", code, ExitPeekedTo.NUM_ENTRIES); 1364 break; 1365 case PanelState.EXPANDED: 1366 code = getStateChangeCode( 1367 toState, reason, EXIT_EXPANDED_TO_STATE_CHANGE_CODES, ExitExpandedTo.OTHER); 1368 RecordHistogram.recordEnumeratedHistogram( 1369 "Search.ContextualSearchExitExpanded", code, ExitExpandedTo.NUM_ENTRIES); 1370 break; 1371 case PanelState.MAXIMIZED: 1372 code = getStateChangeCode(toState, reason, EXIT_MAXIMIZED_TO_STATE_CHANGE_CODES, 1373 ExitMaximizedTo.OTHER); 1374 RecordHistogram.recordEnumeratedHistogram( 1375 "Search.ContextualSearchExitMaximized", code, ExitMaximizedTo.NUM_ENTRIES); 1376 break; 1377 default: 1378 break; 1379 } 1380 } 1381 1382 /** 1383 * Logs the number of impressions and CTR for the previous week for the current user. 1384 * @param previousWeekImpressions The number of times the user saw the Contextual Search Bar. 1385 * @param previousWeekCtr The CTR expressed as a percentage. 1386 */ logPreviousWeekCtr(int previousWeekImpressions, int previousWeekCtr)1387 public static void logPreviousWeekCtr(int previousWeekImpressions, int previousWeekCtr) { 1388 RecordHistogram.recordCountHistogram( 1389 "Search.ContextualSearchPreviousWeekImpressions", previousWeekImpressions); 1390 RecordHistogram.recordPercentageHistogram( 1391 "Search.ContextualSearchPreviousWeekCtr", previousWeekCtr); 1392 } 1393 1394 /** 1395 * Logs the number of impressions and CTR for previous 28-day period for the current user. 1396 * @param previous28DayImpressions The number of times the user saw the Contextual Search Bar. 1397 * @param previous28DayCtr The CTR expressed as a percentage. 1398 */ logPrevious28DayCtr(int previous28DayImpressions, int previous28DayCtr)1399 public static void logPrevious28DayCtr(int previous28DayImpressions, int previous28DayCtr) { 1400 RecordHistogram.recordCountHistogram( 1401 "Search.ContextualSearchPrevious28DayImpressions", previous28DayImpressions); 1402 RecordHistogram.recordPercentageHistogram( 1403 "Search.ContextualSearchPrevious28DayCtr", previous28DayCtr); 1404 } 1405 1406 /** 1407 * Logs a duration since the outcomes (and associated timestamp) were saved in persistent 1408 * storage. 1409 * @param durationMs The duration to log, in milliseconds. 1410 */ logOutcomesTimestamp(long durationMs)1411 public static void logOutcomesTimestamp(long durationMs) { 1412 int durationInDays = (int) (durationMs / DateUtils.DAY_IN_MILLIS); 1413 RecordHistogram.recordCount100Histogram( 1414 "Search.ContextualSearch.OutcomesDuration", durationInDays); 1415 } 1416 1417 /** 1418 * Get the encoded value to use for the Bar Overlap histogram by encoding all the input 1419 * parameters. 1420 * @param didBarOverlap Whether the selection overlapped the Bar position. 1421 * @param wasPanelSeen Whether the panel content was seen. 1422 * @param wasTap Whether the gesture was a Tap. 1423 * @return The value for the enum histogram. 1424 */ getBarOverlapEnum( boolean didBarOverlap, boolean wasPanelSeen, boolean wasTap)1425 private static @BarOverlapResults int getBarOverlapEnum( 1426 boolean didBarOverlap, boolean wasPanelSeen, boolean wasTap) { 1427 if (wasTap) { 1428 if (didBarOverlap) { 1429 return wasPanelSeen ? BarOverlapResults.BAR_OVERLAP_RESULTS_SEEN_FROM_TAP 1430 : BarOverlapResults.BAR_OVERLAP_RESULTS_NOT_SEEN_FROM_TAP; 1431 } else { 1432 return wasPanelSeen ? BarOverlapResults.NO_BAR_OVERLAP_RESULTS_SEEN_FROM_TAP 1433 : BarOverlapResults.NO_BAR_OVERLAP_RESULTS_NOT_SEEN_FROM_TAP; 1434 } 1435 } else { 1436 if (didBarOverlap) { 1437 return wasPanelSeen 1438 ? BarOverlapResults.BAR_OVERLAP_RESULTS_SEEN_FROM_LONG_PRESS 1439 : BarOverlapResults.BAR_OVERLAP_RESULTS_NOT_SEEN_FROM_LONG_PRESS; 1440 } else { 1441 return wasPanelSeen 1442 ? BarOverlapResults.NO_BAR_OVERLAP_RESULTS_SEEN_FROM_LONG_PRESS 1443 : BarOverlapResults.NO_BAR_OVERLAP_RESULTS_NOT_SEEN_FROM_LONG_PRESS; 1444 } 1445 } 1446 } 1447 1448 /** 1449 * Logs whether Contextual Cards data was shown. Should be logged on tap if Contextual 1450 * Cards integration is enabled. 1451 * @param shown Whether Contextual Cards data was shown in the Bar. 1452 */ logContextualCardsDataShown(boolean shown)1453 public static void logContextualCardsDataShown(boolean shown) { 1454 RecordHistogram.recordBooleanHistogram( 1455 "Search.ContextualSearchContextualCardsIntegration.DataShown", shown); 1456 } 1457 1458 /** 1459 * Logs whether results were seen when Contextual Cards data was shown. 1460 * @param wasSeen Whether the search results were seen. 1461 */ logContextualCardsResultsSeen(boolean wasSeen)1462 public static void logContextualCardsResultsSeen(boolean wasSeen) { 1463 RecordHistogram.recordEnumeratedHistogram( 1464 "Search.ContextualSearchContextualCardsIntegration.ResultsSeen", 1465 wasSeen ? Results.SEEN : Results.NOT_SEEN, Results.NUM_ENTRIES); 1466 } 1467 1468 /** 1469 * Logs whether a quick action intent resolved to zero, one, or many apps. 1470 * @param quickActionCategory The {@link QuickActionCategory} for the quick action. 1471 * @param numMatchingAppsApps The number of apps that the resolved intent matched. 1472 */ logQuickActionIntentResolution(int quickActionCategory, int numMatchingAppsApps)1473 public static void logQuickActionIntentResolution(int quickActionCategory, 1474 int numMatchingAppsApps) { 1475 int code = numMatchingAppsApps == 0 1476 ? QuickActionResolve.FAILED 1477 : numMatchingAppsApps == 1 ? QuickActionResolve.SINGLE 1478 : QuickActionResolve.MULTIPLE; 1479 RecordHistogram.recordEnumeratedHistogram( 1480 "Search.ContextualSearchQuickActions.IntentResolution." 1481 + getLabelForQuickActionCategory(quickActionCategory), 1482 code, QuickActionResolve.NUM_ENTRIES); 1483 } 1484 1485 /** 1486 * Logs whether a quick action was shown, and the quick aciton category if a quick action was 1487 * shown. Should be logged on tap if Contextual Search single actions are enabled. 1488 * @param quickActionShown Whether a quick action was shown. 1489 * @param quickActionCategory The {@link QuickActionCategory} for the quick action. 1490 */ logQuickActionShown(boolean quickActionShown, int quickActionCategory)1491 public static void logQuickActionShown(boolean quickActionShown, int quickActionCategory) { 1492 RecordHistogram.recordBooleanHistogram( 1493 "Search.ContextualSearchQuickActions.Shown", quickActionShown); 1494 if (quickActionShown) { 1495 RecordHistogram.recordEnumeratedHistogram( 1496 "Search.ContextualSearchQuickActions.Category", quickActionCategory, 1497 QuickActionCategory.BOUNDARY); 1498 } 1499 } 1500 1501 /** 1502 * Logs whether results were seen when a quick action was present. 1503 * @param wasSeen Whether the search results were seen. 1504 * @param quickActionCategory The {@link QuickActionCategory} for the quick action. 1505 */ logQuickActionResultsSeen(boolean wasSeen, int quickActionCategory)1506 public static void logQuickActionResultsSeen(boolean wasSeen, int quickActionCategory) { 1507 RecordHistogram.recordEnumeratedHistogram("Search.ContextualSearchQuickActions.ResultsSeen." 1508 + getLabelForQuickActionCategory(quickActionCategory), 1509 wasSeen ? Results.SEEN : Results.NOT_SEEN, Results.NUM_ENTRIES); 1510 } 1511 1512 /** 1513 * Logs whether a quick action was clicked. 1514 * @param wasClicked Whether the quick action was clicked 1515 * @param quickActionCategory The {@link QuickActionCategory} for the quick action. 1516 */ logQuickActionClicked(boolean wasClicked, int quickActionCategory)1517 public static void logQuickActionClicked(boolean wasClicked, int quickActionCategory) { 1518 RecordHistogram.recordBooleanHistogram( 1519 "Search.ContextualSearchQuickActions.Clicked." 1520 + getLabelForQuickActionCategory(quickActionCategory), 1521 wasClicked); 1522 } 1523 1524 /** 1525 * Logs the primary CoCa {@link CardTag} for searches where the panel contents was seen, 1526 * including {@codeCardTag.CT_NONE} when no card or tag, and {@codeCardTag.CT_OTHER} when it's 1527 * one we do not recognize. 1528 * @param wasSearchContentViewSeen Whether the panel was seen. 1529 * @param cardTagEnum The primary CoCa card Tag for the result seen. 1530 */ logCardTagSeen(boolean wasSearchContentViewSeen, @CardTag int cardTagEnum)1531 public static void logCardTagSeen(boolean wasSearchContentViewSeen, @CardTag int cardTagEnum) { 1532 if (wasSearchContentViewSeen) { 1533 RecordHistogram.recordEnumeratedHistogram( 1534 "Search.ContextualSearch.CardTagSeen", cardTagEnum, CardTag.NUM_ENTRIES); 1535 } 1536 RecordHistogram.recordEnumeratedHistogram( 1537 "Search.ContextualSearch.CardTag", cardTagEnum, CardTag.NUM_ENTRIES); 1538 } 1539 1540 /** 1541 * Logs results-seen when we have a useful Ranker prediction inference. 1542 * @param wasPanelSeen Whether the panel was seen. 1543 * @param predictionKind An integer reflecting the Ranker prediction, e.g. that this is a good 1544 * time to suppress triggering because the likelihood of opening the panel is relatively 1545 * low. 1546 */ logRankerInference( boolean wasPanelSeen, @AssistRankerPrediction int predictionKind)1547 public static void logRankerInference( 1548 boolean wasPanelSeen, @AssistRankerPrediction int predictionKind) { 1549 if (predictionKind == AssistRankerPrediction.SHOW) { 1550 RecordHistogram.recordEnumeratedHistogram( 1551 "Search.ContextualSearch.Ranker.NotSuppressed.ResultsSeen", 1552 wasPanelSeen ? Results.SEEN : Results.NOT_SEEN, Results.NUM_ENTRIES); 1553 } else if (predictionKind == AssistRankerPrediction.SUPPRESS) { 1554 RecordHistogram.recordEnumeratedHistogram( 1555 "Search.ContextualSearch.Ranker.WouldSuppress.ResultsSeen", 1556 wasPanelSeen ? Results.SEEN : Results.NOT_SEEN, Results.NUM_ENTRIES); 1557 } 1558 } 1559 1560 /** 1561 * Logs Ranker's prediction of whether or not to suppress. 1562 * @param predictionKind An integer reflecting the Ranker prediction, e.g. that this is a good 1563 * time to suppress triggering because the likelihood of opening the panel is relatively 1564 * low. 1565 */ logRankerPrediction(@ssistRankerPrediction int predictionKind)1566 public static void logRankerPrediction(@AssistRankerPrediction int predictionKind) { 1567 // For now we just log whether or not suppression is predicted. 1568 RecordHistogram.recordBooleanHistogram("Search.ContextualSearch.Ranker.Suppressed", 1569 predictionKind == AssistRankerPrediction.SUPPRESS); 1570 } 1571 1572 /** Logs that Ranker recorded a set of features for training or inference. */ logRecordedFeaturesToRanker()1573 public static void logRecordedFeaturesToRanker() { 1574 logRecordedToRanker(false); 1575 } 1576 1577 /** Logs that Ranker recorded a set of outcomes for training or inference. */ logRecordedOutcomesToRanker()1578 public static void logRecordedOutcomesToRanker() { 1579 logRecordedToRanker(true); 1580 } 1581 1582 /** 1583 * Logs that Ranker recorded some data for training or inference. 1584 * @param areOutcomes Whether the data are outcomes. 1585 */ logRecordedToRanker(boolean areOutcomes)1586 private static void logRecordedToRanker(boolean areOutcomes) { 1587 RecordHistogram.recordBooleanHistogram( 1588 "Search.ContextualSearch.Ranker.Recorded", areOutcomes); 1589 } 1590 1591 /** 1592 * Logs that features or outcomes are available to record to Ranker. 1593 * This data can be used to correlate with #logRecordedToRanker to validate that everything that 1594 * should be recorded is actually being recorded. 1595 * @param areOutcomes Whether the features available are outcomes. 1596 */ logRankerFeaturesAvailable(boolean areOutcomes)1597 static void logRankerFeaturesAvailable(boolean areOutcomes) { 1598 RecordHistogram.recordBooleanHistogram( 1599 "Search.ContextualSearch.Ranker.FeaturesAvailable", areOutcomes); 1600 } 1601 1602 /** 1603 * Logs the previous enabled-state of this user before the feature was turned full-on for 1604 * Unified Consent (when integration is enabled). 1605 * @param wasPreviouslyUndecided Whether the user was previously undecided. 1606 */ logUnifiedConsentPreviousEnabledState(boolean wasPreviouslyUndecided)1607 static void logUnifiedConsentPreviousEnabledState(boolean wasPreviouslyUndecided) { 1608 RecordHistogram.recordBooleanHistogram( 1609 "Search.ContextualSearch.UnifiedConsent.PreviouslyUndecided", 1610 wasPreviouslyUndecided); 1611 } 1612 1613 /** 1614 * Logs whether a request will be throttled for Unified Consent integration, for all requests 1615 * regardless of whether the integration feature is enabled. Logged multiple times per request. 1616 * @param isRequestThrottled Whether the current request is being throttled. 1617 */ logUnifiedConsentThrottledRequests(boolean isRequestThrottled)1618 static void logUnifiedConsentThrottledRequests(boolean isRequestThrottled) { 1619 RecordHistogram.recordBooleanHistogram( 1620 "Search.ContextualSearch.UnifiedConsent.ThrottledRequests", isRequestThrottled); 1621 } 1622 1623 /** 1624 * Logs whether this user was eligible for throttling of requests when Unified Consent 1625 * integration is enabled and throttling is in effect. 1626 * @param isThrottleEligible Whether this user is eligible to be throttled. 1627 */ logUnifiedConsentThrottleEligible(boolean isThrottleEligible)1628 static void logUnifiedConsentThrottleEligible(boolean isThrottleEligible) { 1629 RecordHistogram.recordBooleanHistogram( 1630 "Search.ContextualSearch.UnifiedConsent.ThrottleEligible", isThrottleEligible); 1631 } 1632 1633 /** 1634 * Logs a histogram indicating which privacy permissions are available that Related Searches 1635 * cares about. This ignores any language constraint. 1636 * <p>This can be called multiple times for each user from any part of the code that's freqently 1637 * executed. 1638 * @param canSendUrl Whether this user has allowed sending page URL info to Google. 1639 * @param canSendContent Whether the user can send page content to Google (has accepted the 1640 * Contextual Search opt-in). 1641 */ logRelatedSearchesPermissionsForAllUsers( boolean canSendUrl, boolean canSendContent)1642 static void logRelatedSearchesPermissionsForAllUsers( 1643 boolean canSendUrl, boolean canSendContent) { 1644 @Permissions 1645 int permissionsEnum; 1646 if (canSendUrl) { 1647 permissionsEnum = 1648 canSendContent ? Permissions.SEND_URL_AND_CONTENT : Permissions.SEND_URL; 1649 } else { 1650 permissionsEnum = canSendContent ? Permissions.SEND_CONTENT : Permissions.SEND_NOTHING; 1651 } 1652 RecordHistogram.recordEnumeratedHistogram("Search.RelatedSearches.AllUserPermissions", 1653 permissionsEnum, Permissions.NUM_ENTRIES); 1654 } 1655 1656 /** 1657 * Logs a histogram indicating that a user is qualified for the Related Searches experiment 1658 * regardless of whether that feature is enabled. This uses a boolean histogram but always 1659 * logs true in order to get a raw bucket count (without using a user action, as suggested 1660 * in the User Action Guidelines doc). 1661 * <p>We use this to gauge whether each group has a balanced number of qualified users. 1662 * Can be logged multiple times since we'll just look at the user-count of this histogram. 1663 * This should be called any time a gesture is detected that could trigger a Related Search 1664 * if the feature were enabled. 1665 */ logRelatedSearchesQualifiedUsers()1666 static void logRelatedSearchesQualifiedUsers() { 1667 RecordHistogram.recordBooleanHistogram("Search.RelatedSearches.QualifiedUsers", true); 1668 } 1669 1670 /** 1671 * Gets the state-change code for the given parameters by doing a lookup in the given map. 1672 * @param state The panel state. 1673 * @param reason The reason the state changed. 1674 * @param stateChangeCodes The map of state and reason to code. 1675 * @param defaultCode The code to return if the given values are not found in the map. 1676 * @return The code to write into an enum histogram, based on the given map. 1677 */ getStateChangeCode(@anelState int state, @StateChangeReason int reason, Map<StateChangeKey, Integer> stateChangeCodes, int defaultCode)1678 private static int getStateChangeCode(@PanelState int state, @StateChangeReason int reason, 1679 Map<StateChangeKey, Integer> stateChangeCodes, int defaultCode) { 1680 Integer code = stateChangeCodes.get(new StateChangeKey(state, reason)); 1681 return code != null ? code : defaultCode; 1682 } 1683 1684 /** 1685 * Gets the panel-seen code for the given parameters by doing a lookup in the seen-by-gesture 1686 * map. 1687 * @param wasPanelSeen Whether the panel was seen. 1688 * @param wasTap Whether the gesture that originally caused the panel to show was a Tap. 1689 * @return The code to write into a panel-seen histogram. 1690 */ getPanelSeenByGestureStateCode(boolean wasPanelSeen, boolean wasTap)1691 private static int getPanelSeenByGestureStateCode(boolean wasPanelSeen, boolean wasTap) { 1692 return SEEN_BY_GESTURE_CODES.get(new Pair<Boolean, Boolean>(wasPanelSeen, wasTap)); 1693 } 1694 1695 /** 1696 * Gets the promo-outcome code for the given parameter by doing a lookup in the 1697 * promo-by-gesture map. 1698 * @param preferenceValue The code for the current preference value. 1699 * @param wasTap Whether the gesture that originally caused the panel to show was a Tap. 1700 * @return The code to write into a promo-outcome histogram. 1701 */ getPromoByGestureStateCode(int preferenceValue, boolean wasTap)1702 private static int getPromoByGestureStateCode(int preferenceValue, boolean wasTap) { 1703 return PROMO_BY_GESTURE_CODES.get(new Pair<Integer, Boolean>(preferenceValue, wasTap)); 1704 } 1705 1706 /** 1707 * @return The code for the Contextual Search preference. 1708 */ getPreferenceValue()1709 private static int getPreferenceValue() { 1710 if (ContextualSearchManager.isContextualSearchUninitialized()) { 1711 return Preference.UNINITIALIZED; 1712 } else if (ContextualSearchManager.isContextualSearchDisabled()) { 1713 return Preference.DISABLED; 1714 } 1715 return Preference.ENABLED; 1716 } 1717 1718 /** 1719 * Gets the encode value for quick answers seen. 1720 * @param didActivate Whether the quick answer was shown. 1721 * @param didAnswer Whether the caption was a full answer, not just a hint. 1722 * @param wasSeen Whether the search panel was opened. 1723 * @return The encoded value. 1724 */ getQuickAnswerSeenValue( boolean didActivate, boolean didAnswer, boolean wasSeen)1725 private static @QuickAnswerSeen int getQuickAnswerSeenValue( 1726 boolean didActivate, boolean didAnswer, boolean wasSeen) { 1727 if (wasSeen) { 1728 if (didActivate) { 1729 return didAnswer ? QuickAnswerSeen.ACTIVATED_WAS_AN_ANSWER_SEEN 1730 : QuickAnswerSeen.ACTIVATED_NOT_AN_ANSWER_SEEN; 1731 } else { 1732 return QuickAnswerSeen.NOT_ACTIVATED_SEEN; 1733 } 1734 } else { 1735 if (didActivate) { 1736 return didAnswer ? QuickAnswerSeen.ACTIVATED_WAS_AN_ANSWER_NOT_SEEN 1737 : QuickAnswerSeen.ACTIVATED_NOT_AN_ANSWER_NOT_SEEN; 1738 } else { 1739 return QuickAnswerSeen.NOT_ACTIVATED_NOT_SEEN; 1740 } 1741 } 1742 } 1743 1744 /** 1745 * Logs to a seen-by-gesture histogram of the given name. 1746 * @param wasPanelSeen Whether the panel was seen. 1747 * @param wasTap Whether the gesture that originally caused the panel to show was a Tap. 1748 * @param histogramName The full name of the histogram to log to. 1749 */ logHistogramByGesture(boolean wasPanelSeen, boolean wasTap, String histogramName)1750 private static void logHistogramByGesture(boolean wasPanelSeen, boolean wasTap, 1751 String histogramName) { 1752 RecordHistogram.recordEnumeratedHistogram(histogramName, 1753 getPanelSeenByGestureStateCode(wasPanelSeen, wasTap), ResultsByGesture.NUM_ENTRIES); 1754 } 1755 getLabelForQuickActionCategory(int quickActionCategory)1756 private static String getLabelForQuickActionCategory(int quickActionCategory) { 1757 switch(quickActionCategory) { 1758 case QuickActionCategory.ADDRESS: 1759 return "Address"; 1760 case QuickActionCategory.EMAIL: 1761 return "Email"; 1762 case QuickActionCategory.EVENT: 1763 return "Event"; 1764 case QuickActionCategory.PHONE: 1765 return "Phone"; 1766 case QuickActionCategory.WEBSITE: 1767 return "Website"; 1768 default: 1769 return "None"; 1770 } 1771 } 1772 } 1773