blob: be743ff2d33cfc6713d2fe01475b13975c9200ac [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.omnibox;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.TimingMetric;
import org.chromium.chrome.browser.omnibox.suggestions.mostvisited.SuggestTileType;
import org.chromium.components.metrics.OmniboxEventProtos.OmniboxEventProto.PageClassification;
import org.chromium.components.omnibox.EntityInfoProto.ActionInfo.ActionType;
import org.chromium.components.omnibox.action.ActionInSuggestUmaType;
import org.chromium.components.omnibox.action.OmniboxPedalType;
import org.chromium.components.omnibox.suggestions.OmniboxSuggestionUiType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* This class collects a variety of different Omnibox related metrics.
*/
public class OmniboxMetrics {
/**
* Maximum number of suggest tile types we want to record.
* Anything beyond this will be reported in the overflow bucket.
*/
private static final int MAX_SUGGEST_TILE_TYPE_POSITION = 15;
public static final int MAX_AUTOCOMPLETE_POSITION = 30;
/**
* Duration between the request for suggestions and the time the first (synchronous) reply is
* converted to the UI model.
*/
public static final String HISTOGRAM_SUGGESTIONS_REQUEST_TO_UI_MODEL_FIRST =
"Android.Omnibox.SuggestionList.RequestToUiModel.First";
/**
* Duration between the request for suggestions and the time the last (asynchronous) reply is
* converted to the UI model.
*/
public static final String HISTOGRAM_SUGGESTIONS_REQUEST_TO_UI_MODEL_LAST =
"Android.Omnibox.SuggestionList.RequestToUiModel.Last";
@IntDef({RefineActionUsage.NOT_USED, RefineActionUsage.SEARCH_WITH_ZERO_PREFIX,
RefineActionUsage.SEARCH_WITH_PREFIX, RefineActionUsage.SEARCH_WITH_BOTH,
RefineActionUsage.COUNT})
@Retention(RetentionPolicy.SOURCE)
public @interface RefineActionUsage {
int NOT_USED = 0; // User did not interact with Refine button.
int SEARCH_WITH_ZERO_PREFIX = 1; // User interacted with Refine button in zero-prefix mode.
int SEARCH_WITH_PREFIX = 2; // User interacted with Refine button in non-zero-prefix mode.
int SEARCH_WITH_BOTH = 3; // User interacted with Refine button in both contexts.
int COUNT = 4;
}
@IntDef({ActionInSuggestIntentResult.SUCCESS, ActionInSuggestIntentResult.BAD_URI_SYNTAX,
ActionInSuggestIntentResult.ACTIVITY_NOT_FOUND, ActionInSuggestIntentResult.COUNT})
@Retention(RetentionPolicy.SOURCE)
public @interface ActionInSuggestIntentResult {
/// Intent started successfully.
int SUCCESS = 0;
/// Unable to deserialize intent: invalid syntax. Never recorded: http://b/279756377.
int BAD_URI_SYNTAX = 1;
/// Unable to start intent: no activity.
int ACTIVITY_NOT_FOUND = 2;
int COUNT = 3;
}
// TODO(crbug/1418077) export this from upstream.
// See entity_info.proto, ActionType.
private static final int ACTION_TYPE_COUNT = 20;
/**
* Record how long the Suggestion List needed to layout its content and children.
*/
public static TimingMetric recordSuggestionListLayoutTime() {
return TimingMetric.shortThreadTime("Android.Omnibox.SuggestionList.LayoutTime2");
}
/**
* Record how long the Suggestion List needed to measure its content and children.
*/
public static TimingMetric recordSuggestionListMeasureTime() {
return TimingMetric.shortThreadTime("Android.Omnibox.SuggestionList.MeasureTime2");
}
/**
* Record the amount of time needed to create a new suggestion view.
* The type of view is intentionally ignored for this call.
*/
public static TimingMetric recordSuggestionViewCreateTime() {
return TimingMetric.shortThreadTime("Android.Omnibox.SuggestionView.CreateTime2");
}
/**
* Record whether suggestion view was successfully reused.
*
* @param viewsCreated Number of views created during the input session. This should not be
* higher than the sum of all limits in HistogramRecordingRecycledViewPool.
* @param viewsReused Ratio of views re-used to total views bound. Effectively captures the
* efficiency of view recycling.
*/
public static void recordSuggestionViewReuseStats(int viewsCreated, int viewsReused) {
RecordHistogram.recordCount100Histogram(
"Android.Omnibox.SuggestionView.SessionViewsCreated", viewsCreated);
RecordHistogram.recordCount100Histogram(
"Android.Omnibox.SuggestionView.SessionViewsReused", viewsReused);
}
/**
* Record the type of the suggestion view that had to be constructed.
* Recorded view type could not be retrieved from the Recycled View Pool and had to
* be re-created.
* Relevant for Omnibox recycler view improvements.
*
* @param type The type of view that needed to be recreated.
*/
public static void recordSuggestionsViewCreatedType(@OmniboxSuggestionUiType int type) {
RecordHistogram.recordEnumeratedHistogram(
"Android.Omnibox.SuggestionView.CreatedType", type, OmniboxSuggestionUiType.COUNT);
}
/**
* Record the type of the suggestion view that was re-used.
* Recorded view type was retrieved from the Recycled View Pool.
* Relevant for Omnibox recycler view improvements.
*
* @param type The type of view that was reused from pool.
*/
public static void recordSuggestionsViewReusedType(@OmniboxSuggestionUiType int type) {
RecordHistogram.recordEnumeratedHistogram(
"Android.Omnibox.SuggestionView.ReusedType", type, OmniboxSuggestionUiType.COUNT);
}
/**
* Record whether the interaction with the Omnibox resulted with a navigation (true) or user
* leaving the omnibox and suggestions list.
*
* @param focusResultedInNavigation Whether the user completed interaction with navigation.
*/
public static void recordOmniboxFocusResultedInNavigation(boolean focusResultedInNavigation) {
RecordHistogram.recordBooleanHistogram(
"Omnibox.FocusResultedInNavigation", focusResultedInNavigation);
}
/**
* Record the length of time between when omnibox gets focused and when a omnibox match is open.
*/
public static void recordFocusToOpenTime(long focusToOpenTimeInMillis) {
RecordHistogram.recordMediumTimesHistogram(
"Omnibox.FocusToOpenTimeAnyPopupState3", focusToOpenTimeInMillis);
}
/**
* Record whether the used suggestion originates from Cache or Autocomplete subsystem.
*
* @param isFromCache Whether the suggestion selected by the User comes from suggestion cache.
*/
public static void recordUsedSuggestionFromCache(boolean isFromCache) {
RecordHistogram.recordBooleanHistogram(
"Android.Omnibox.UsedSuggestionFromCache", isFromCache);
}
/**
* Record the Refine action button usage.
* Unlike the MobileOmniobxRefineSuggestion UserAction, this is recorded only once at the end of
* an Omnibox interaction, and includes the cases where the user has not interacted with the
* Refine button at all.
*
* @param refineActionUsage Whether - and how Refine action button was used.
*/
public static void recordRefineActionUsage(@RefineActionUsage int refineActionUsage) {
RecordHistogram.recordEnumeratedHistogram(
"Android.Omnibox.RefineActionUsage", refineActionUsage, RefineActionUsage.COUNT);
}
/**
* Record the pedal shown when the user used the omnibox to go somewhere.
*
* @param type the shown pedal's {@link OmniboxActionType}.
*/
public static void recordPedalShown(@OmniboxPedalType int type) {
RecordHistogram.recordEnumeratedHistogram(
"Omnibox.PedalShown", type, OmniboxPedalType.TOTAL_COUNT);
}
/**
* Record a pedal is used.
*
@param omniboxActionType the clicked pedal's {@link OmniboxActionType}.
*/
public static void recordPedalUsed(@OmniboxPedalType int type) {
RecordHistogram.recordEnumeratedHistogram(
"Omnibox.SuggestionUsed.Pedal", type, OmniboxPedalType.TOTAL_COUNT);
}
/**
* Record page class specific histogram reflecting whether the user scrolled the suggestions
* list.
*
* @param pageClass Page classification.
* @param wasScrolled Whether the suggestions list was scrolled.
*/
public static void recordSuggestionsListScrolled(int pageClass, boolean wasScrolled) {
RecordHistogram.recordBooleanHistogram(
histogramName("Android.Omnibox.SuggestionsListScrolled", pageClass), wasScrolled);
}
/**
* Record the kind of (MostVisitedURL/OrganicRepeatableSearch) Tile type the User opened.
*
* @param position The position of a tile in the carousel.
* @param isSearchTile Whether tile being opened is a Search tile.
*/
public static void recordSuggestTileTypeUsed(int position, boolean isSearchTile) {
@SuggestTileType
int tileType = isSearchTile ? SuggestTileType.SEARCH : SuggestTileType.URL;
RecordHistogram.recordExactLinearHistogram(
"Omnibox.SuggestTiles.SelectedTileIndex", position, MAX_SUGGEST_TILE_TYPE_POSITION);
RecordHistogram.recordEnumeratedHistogram(
"Omnibox.SuggestTiles.SelectedTileType", tileType, SuggestTileType.COUNT);
}
/**
* Records relevant histogram(s) when a Journeys action is clicked in the omnibox. Not emitted
* if the given position is <0.
*/
public static void recordResumeJourneyClick(int position) {
if (position < 0) return;
RecordHistogram.recordExactLinearHistogram(
"Omnibox.SuggestionUsed.ResumeJourney", position, MAX_AUTOCOMPLETE_POSITION);
}
/**
* Records relevant histogram(s) when a Journeys action is shown in the omnibox. Not emitted if
* the given position is <0.
*/
public static void recordResumeJourneyShown(int position) {
if (position < 0) return;
RecordHistogram.recordEnumeratedHistogram(
"Omnibox.ResumeJourneyShown", position, MAX_AUTOCOMPLETE_POSITION);
}
/**
* Records the time elapsed between the two events:
* - the suggestions were requested (as a result of User input), and
* - the suggestions response was transformed to a UI model.
*
* @param isFirst specifies whether this is the first (synchronous), or the last (final)
* asynchronous, suggestions response received from the AutocompleteController
* @param elapsedTimeMs specifies how much time has elapsed between the two events
*/
public static void recordSuggestionRequestToModelTime(boolean isFirst, long elapsedTimeMs) {
RecordHistogram.recordCustomTimesHistogram(isFirst
? HISTOGRAM_SUGGESTIONS_REQUEST_TO_UI_MODEL_FIRST
: HISTOGRAM_SUGGESTIONS_REQUEST_TO_UI_MODEL_LAST,
elapsedTimeMs, 1, 1000, 50);
}
/**
* Record the outcome of ActionInSuggest chip interaction.
*
* @param intentResult the {@link #ActionInSuggestIntentResult} to record
*/
public static final void recordActionInSuggestIntentResult(
@ActionInSuggestIntentResult int intentResult) {
RecordHistogram.recordEnumeratedHistogram("Android.Omnibox.ActionInSuggest.IntentResult",
intentResult, ActionInSuggestIntentResult.COUNT);
}
/**
* Record the Use of the Omnibox Action in Suggest.
*
* @param actionType the direct value of corresponding {@link
* EntityInfoProto.ActionInfo.ActionType}
* @param isUsed whether the suggestion was clicked
*/
public static void recordActionInSuggestUsed(int actionType) {
RecordHistogram.recordEnumeratedHistogram("Omnibox.ActionInSuggest.Used",
actionTypeToUmaType(actionType), ActionInSuggestUmaType.MAX_VALUE);
}
/**
* Record the Presence of the Omnibox Action in Suggest.
*
* @param actionType the direct value of corresponding {@link
* EntityInfoProto.ActionInfo.ActionType}
* @param isUsed whether the suggestion was visible
*/
public static void recordActionInSuggestShown(int actionType) {
RecordHistogram.recordEnumeratedHistogram("Omnibox.ActionInSuggest.Shown",
actionTypeToUmaType(actionType), ActionInSuggestUmaType.MAX_VALUE);
}
/**
* Translate ActionType to ActionInSuggestUmaType.
*
* @param type the type of Action in Suggest to translate.
*/
private static @ActionInSuggestUmaType int actionTypeToUmaType(int type) {
switch (type) {
case ActionType.CALL_VALUE:
return ActionInSuggestUmaType.CALL;
case ActionType.DIRECTIONS_VALUE:
return ActionInSuggestUmaType.DIRECTIONS;
case ActionType.WEBSITE_VALUE:
return ActionInSuggestUmaType.WEBSITE;
case ActionType.REVIEWS_VALUE:
return ActionInSuggestUmaType.REVIEWS;
default:
return ActionInSuggestUmaType.UNKNOWN;
}
}
/**
* Translate the pageClass to a histogram suffix.
*
* @param histogram Histogram prefix.
* @param pageClass Page classification to translate.
* @return Metric name.
*/
private static String histogramName(@NonNull String prefix, int pageClass) {
String suffix = "Other";
switch (pageClass) {
case PageClassification.NTP_VALUE:
case PageClassification.INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS_VALUE:
case PageClassification.INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS_VALUE:
suffix = "NTP";
break;
case PageClassification.SEARCH_RESULT_PAGE_DOING_SEARCH_TERM_REPLACEMENT_VALUE:
case PageClassification.SEARCH_RESULT_PAGE_NO_SEARCH_TERM_REPLACEMENT_VALUE:
suffix = "SRP";
break;
case PageClassification.ANDROID_SEARCH_WIDGET_VALUE:
case PageClassification.ANDROID_SHORTCUTS_WIDGET_VALUE:
suffix = "Widget";
break;
case PageClassification.BLANK_VALUE:
case PageClassification.HOME_PAGE_VALUE:
case PageClassification.OTHER_VALUE:
// use default value for websites.
break;
default:
// Report an error, but fall back to a default value.
// Use this to detect missing new cases.
// TODO(crbug.com/1314765): This assert fails persistently on tablets.
// assert false : "Unknown page classification: " + pageClass;
break;
}
return prefix + "." + suffix;
}
}