[go: nahoru, domu]

blob: 560adad46da1bdc08449044a5f19ad35d8dc7620 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/ui/ntp/feed_metrics_recorder.h"
#import "base/mac/foundation_util.h"
#import "base/metrics/histogram_functions.h"
#import "base/metrics/histogram_macros.h"
#import "base/metrics/user_metrics.h"
#import "base/metrics/user_metrics_action.h"
#include "base/time/time.h"
#import "components/feed/core/v2/public/common_enums.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
using feed::FeedEngagementType;
using feed::FeedUserActionType;
// Values for the UMA ContentSuggestions.Feed.LoadStreamStatus.LoadMore
// histogram. These values are persisted to logs. Entries should not be
// renumbered and numeric values should never be reused. This must be kept
// in sync with FeedLoadStreamStatus in enums.xml.
enum class FeedLoadStreamStatus {
kNoStatus = 0,
kLoadedFromStore = 1,
// Bottom of feed was reached, triggering infinite feed.
kLoadedFromNetwork = 2,
kFailedWithStoreError = 3,
kNoStreamDataInStore = 4,
kModelAlreadyLoaded = 5,
kNoResponseBody = 6,
kProtoTranslationFailed = 7,
kDataInStoreIsStale = 8,
kDataInStoreIsStaleTimestampInFuture = 9,
kCannotLoadFromNetworkSupressedForHistoryDelete_DEPRECATED = 10,
kCannotLoadFromNetworkOffline = 11,
kCannotLoadFromNetworkThrottled = 12,
kLoadNotAllowedEulaNotAccepted = 13,
kLoadNotAllowedArticlesListHidden = 14,
kCannotParseNetworkResponseBody = 15,
kLoadMoreModelIsNotLoaded = 16,
kLoadNotAllowedDisabledByEnterprisePolicy = 17,
kNetworkFetchFailed = 18,
kCannotLoadMoreNoNextPageToken = 19,
// Highest enumerator. Recommended by Histogram metrics best practices.
kMaxValue = kCannotLoadMoreNoNextPageToken,
};
namespace {
// User action names for the device orientation having changed.
const char kDiscoverFeedHistogramDeviceOrientationChangedToPortrait[] =
"ContentSuggestions.Feed.DeviceOrientationChanged.Portrait";
const char kDiscoverFeedHistogramDeviceOrientationChangedToLandscape[] =
"ContentSuggestions.Feed.DeviceOrientationChanged.Landscape";
// Histogram name for the Discover feed user actions.
const char kDiscoverFeedUserActionHistogram[] =
"ContentSuggestions.Feed.UserActions";
// Histogram name for the Discover feed user actions commands.
const char kDiscoverFeedUserActionCommandHistogram[] =
"ContentSuggestions.Feed.UserActions.Commands";
// User action names for toggling the feed visibility from the header menu.
const char kDiscoverFeedUserActionTurnOn[] =
"Suggestions.ExpandableHeader.Expanded";
const char kDiscoverFeedUserActionTurnOff[] =
"Suggestions.ExpandableHeader.Collapsed";
// User action names for feed back of card items.
const char kDiscoverFeedUserActionLearnMoreTapped[] =
"ContentSuggestions.Feed.CardAction.LearnMore";
const char kDiscoverFeedUserActionOpenSameTab[] =
"ContentSuggestions.Feed.CardAction.Open";
const char kDiscoverFeedUserActionOpenIncognitoTab[] =
"ContentSuggestions.Feed.CardAction.OpenInNewIncognitoTab";
const char kDiscoverFeedUserActionOpenNewTab[] =
"ContentSuggestions.Feed.CardAction.OpenInNewTab";
const char kDiscoverFeedUserActionReadLaterTapped[] =
"ContentSuggestions.Feed.CardAction.ReadLater";
const char kDiscoverFeedUserActionSendFeedbackOpened[] =
"ContentSuggestions.Feed.CardAction.SendFeedback";
const char kDiscoverFeedUserActionContextMenuOpened[] =
"ContentSuggestions.Feed.CardAction.ContextMenu";
const char kDiscoverFeedUserActionHideStory[] =
"ContentSuggestions.Feed.CardAction.HideStory";
const char kDiscoverFeedUserActionCloseContextMenu[] =
"ContentSuggestions.Feed.CardAction.ClosedContextMenu";
const char kDiscoverFeedUserActionNativeActionSheetOpened[] =
"ContentSuggestions.Feed.CardAction.OpenNativeActionSheet";
const char kDiscoverFeedUserActionNativeContextMenuOpened[] =
"ContentSuggestions.Feed.CardAction.OpenNativeContextMenu";
const char kDiscoverFeedUserActionNativeContextMenuClosed[] =
"ContentSuggestions.Feed.CardAction.CloseNativeContextMenu";
const char kDiscoverFeedUserActionNativePulldownMenuOpened[] =
"ContentSuggestions.Feed.CardAction.OpenNativePulldownMenu";
const char kDiscoverFeedUserActionNativePulldownMenuClosed[] =
"ContentSuggestions.Feed.CardAction.CloseNativePulldownMenu";
const char kDiscoverFeedUserActionReportContentOpened[] =
"ContentSuggestions.Feed.CardAction.ReportContent";
const char kDiscoverFeedUserActionReportContentClosed[] =
"ContentSuggestions.Feed.CardAction.ClosedReportContent";
const char kDiscoverFeedUserActionPreviewTapped[] =
"ContentSuggestions.Feed.CardAction.TapPreview";
// User action names for feed header menu.
const char kDiscoverFeedUserActionManageTapped[] =
"ContentSuggestions.Feed.HeaderAction.Manage";
const char kDiscoverFeedUserActionManageActivityTapped[] =
"ContentSuggestions.Feed.HeaderAction.ManageActivity";
const char kDiscoverFeedUserActionManageInterestsTapped[] =
"ContentSuggestions.Feed.HeaderAction.ManageInterests";
const char kDiscoverFeedUserActionManageHiddenTapped[] =
"ContentSuggestions.Feed.HeaderAction.ManageHidden";
const char kDiscoverFeedUserActionManageFollowingTapped[] =
"ContentSuggestions.Feed.HeaderAction.ManageFollowing";
// User action names for management surface.
const char kDiscoverFeedUserActionManagementTappedUnfollow[] =
"ContentSuggestions.Feed.Management.TappedUnfollow";
const char
kDiscoverFeedUserActionManagementTappedRefollowAfterUnfollowOnSnackbar[] =
"ContentSuggestions.Feed.Management."
"TappedRefollowAfterUnfollowOnSnackbar";
const char kDiscoverFeedUserActionManagementTappedUnfollowTryAgainOnSnackbar[] =
"ContentSuggestions.Feed.Management.TappedUnfollowTryAgainOnSnackbar";
// User action name for engaging with feed.
const char kDiscoverFeedUserActionEngaged[] = "ContentSuggestions.Feed.Engaged";
// User action indicating that the feed will refresh.
const char kFeedWillRefresh[] = "ContentSuggestions.Feed.WillRefresh";
// User action triggered when the NTP view hierarchy was fixed after being
// detected as broken.
// TODO(crbug.com/1262536): Remove this when issue is fixed.
const char kNTPViewHierarchyFixed[] = "NewTabPage.ViewHierarchyFixed";
// Histogram name for the feed engagement types.
const char kDiscoverFeedEngagementTypeHistogram[] =
"ContentSuggestions.Feed.EngagementType";
// Histogram name to capture Feed Notice card impressions.
const char kDiscoverFeedNoticeCardFulfilled[] =
"ContentSuggestions.Feed.NoticeCardFulfilled2";
// Histogram name to measure the time it took the Feed to fetch articles
// successfully.
const char kDiscoverFeedArticlesFetchNetworkDurationSuccess[] =
"ContentSuggestions.Feed.Network.Duration.ArticlesFetchSuccess";
// Histogram name to measure the time it took the Feed to fetch articles
// unsuccessfully.
const char kDiscoverFeedArticlesFetchNetworkDurationFailure[] =
"ContentSuggestions.Feed.Network.Duration.ArticlesFetchFailure";
// Histogram name to measure the time it took the Feed to fetch more articles
// successfully.
const char kDiscoverFeedMoreArticlesFetchNetworkDurationSuccess[] =
"ContentSuggestions.Feed.Network.Duration.MoreArticlesFetchSuccess";
// Histogram name to measure the time it took the Feed to fetch more articles
// unsuccessfully.
const char kDiscoverFeedMoreArticlesFetchNetworkDurationFailure[] =
"ContentSuggestions.Feed.Network.Duration.MoreArticlesFetchFailure";
// Histogram name to measure the time it took the Feed to upload actions
// successfully.
const char kDiscoverFeedUploadActionsNetworkDurationSuccess[] =
"ContentSuggestions.Feed.Network.Duration.ActionUploadSuccess";
// Histogram name to measure the time it took the Feed to upload actions
// unsuccessfully.
const char kDiscoverFeedUploadActionsNetworkDurationFailure[] =
"ContentSuggestions.Feed.Network.Duration.ActionUploadFailure";
// Histogram name to measure the time it took the Feed to perform a network
// operation.
const char kDiscoverFeedNetworkDuration[] =
"ContentSuggestions.Feed.Network.Duration";
// Histogram name to measure opened URL's regardless of the surface they were
// opened in.
const char kDiscoverFeedURLOpened[] = "NewTabPage.ContentSuggestions.Opened";
// Histogram name to capture if the last Feed fetch had logging enabled.
const char kDiscoverFeedActivityLoggingEnabled[] =
"ContentSuggestions.Feed.ActivityLoggingEnabled";
// Histogram name for broken NTP view hierarchy logs.
// TODO(crbug.com/1262536): Remove this when issue is fixed.
const char kDiscoverFeedBrokenNTPHierarchy[] =
"ContentSuggestions.Feed.BrokenNTPHierarchy";
// Minimum scrolling amount to record a FeedEngagementType::kFeedEngaged due to
// scrolling.
const int kMinScrollThreshold = 160;
// Time between two metrics recorded to consider it a new session.
const int kMinutesBetweenSessions = 5;
} // namespace
@interface FeedMetricsRecorder ()
// Tracking property to avoid duplicate recordings of
// FeedEngagementType::kFeedEngagedSimple.
@property(nonatomic, assign) BOOL engagedSimpleReported;
// Tracking property to avoid duplicate recordings of
// FeedEngagementType::kFeedEngaged.
@property(nonatomic, assign) BOOL engagedReported;
// Tracking property to avoid duplicate recordings of
// FeedEngagementType::kFeedScrolled.
@property(nonatomic, assign) BOOL scrolledReported;
// The time when the first metric is being recorded for this session.
@property(nonatomic, assign) base::Time sessionStartTime;
@end
@implementation FeedMetricsRecorder
#pragma mark - Public
- (void)recordFeedScrolled:(int)scrollDistance {
[self recordEngagement:scrollDistance interacted:NO];
if (!self.scrolledReported) {
[self recordEngagementTypeHistogram:FeedEngagementType::kFeedScrolled];
self.scrolledReported = YES;
}
}
- (void)recordDeviceOrientationChanged:(UIDeviceOrientation)orientation {
if (orientation == UIDeviceOrientationPortrait) {
base::RecordAction(base::UserMetricsAction(
kDiscoverFeedHistogramDeviceOrientationChangedToPortrait));
} else if (orientation == UIDeviceOrientationLandscapeLeft ||
orientation == UIDeviceOrientationLandscapeRight) {
base::RecordAction(base::UserMetricsAction(
kDiscoverFeedHistogramDeviceOrientationChangedToLandscape));
}
}
- (void)recordDiscoverFeedPreviewTapped {
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kTappedDiscoverFeedPreview];
base::RecordAction(
base::UserMetricsAction(kDiscoverFeedUserActionPreviewTapped));
}
- (void)recordHeaderMenuLearnMoreTapped {
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kTappedLearnMore];
base::RecordAction(
base::UserMetricsAction(kDiscoverFeedUserActionLearnMoreTapped));
}
- (void)recordHeaderMenuManageTapped {
[self
recordDiscoverFeedUserActionHistogram:FeedUserActionType::kTappedManage];
base::RecordAction(
base::UserMetricsAction(kDiscoverFeedUserActionManageTapped));
}
- (void)recordHeaderMenuManageActivityTapped {
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kTappedManageActivity];
base::RecordAction(
base::UserMetricsAction(kDiscoverFeedUserActionManageActivityTapped));
}
- (void)recordHeaderMenuManageInterestsTapped {
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kTappedManageInterests];
base::RecordAction(
base::UserMetricsAction(kDiscoverFeedUserActionManageInterestsTapped));
}
- (void)recordHeaderMenuManageHiddenTapped {
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kTappedManageHidden];
base::RecordAction(
base::UserMetricsAction(kDiscoverFeedUserActionManageHiddenTapped));
}
- (void)recordHeaderMenuManageFollowingTapped {
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kTappedManageFollowing];
base::RecordAction(
base::UserMetricsAction(kDiscoverFeedUserActionManageFollowingTapped));
}
- (void)recordManagementTappedUnfollow {
[self recordDiscoverFeedUserActionHistogram:
FeedUserActionType::kTappedUnfollowOnManagementSurface];
base::RecordAction(
base::UserMetricsAction(kDiscoverFeedUserActionManagementTappedUnfollow));
}
- (void)recordManagementTappedRefollowAfterUnfollowOnSnackbar {
[self recordDiscoverFeedUserActionHistogram:
FeedUserActionType::kTappedRefollowAfterUnfollowOnSnackbar];
base::RecordAction(base::UserMetricsAction(
kDiscoverFeedUserActionManagementTappedRefollowAfterUnfollowOnSnackbar));
}
- (void)recordManagementTappedUnfollowTryAgainOnSnackbar {
[self recordDiscoverFeedUserActionHistogram:
FeedUserActionType::kTappedUnfollowTryAgainOnSnackbar];
base::RecordAction(base::UserMetricsAction(
kDiscoverFeedUserActionManagementTappedUnfollowTryAgainOnSnackbar));
}
- (void)recordDiscoverFeedVisibilityChanged:(BOOL)visible {
if (visible) {
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kTappedTurnOn];
base::RecordAction(base::UserMetricsAction(kDiscoverFeedUserActionTurnOn));
} else {
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kTappedTurnOff];
base::RecordAction(base::UserMetricsAction(kDiscoverFeedUserActionTurnOff));
}
}
- (void)recordOpenURLInSameTab {
[self
recordDiscoverFeedUserActionHistogram:FeedUserActionType::kTappedOnCard];
base::RecordAction(
base::UserMetricsAction(kDiscoverFeedUserActionOpenSameTab));
[self recordOpenURL];
}
- (void)recordOpenURLInNewTab {
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kTappedOpenInNewTab];
base::RecordAction(
base::UserMetricsAction(kDiscoverFeedUserActionOpenNewTab));
[self recordOpenURL];
}
- (void)recordOpenURLInIncognitoTab {
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kTappedOpenInNewIncognitoTab];
base::RecordAction(
base::UserMetricsAction(kDiscoverFeedUserActionOpenIncognitoTab));
[self recordOpenURL];
}
- (void)recordAddURLToReadLater {
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kAddedToReadLater];
base::RecordAction(
base::UserMetricsAction(kDiscoverFeedUserActionReadLaterTapped));
}
- (void)recordTapSendFeedback {
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kTappedSendFeedback];
base::RecordAction(
base::UserMetricsAction(kDiscoverFeedUserActionSendFeedbackOpened));
}
- (void)recordOpenBackOfCardMenu {
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kOpenedContextMenu];
base::RecordAction(
base::UserMetricsAction(kDiscoverFeedUserActionContextMenuOpened));
}
- (void)recordCloseBackOfCardMenu {
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kClosedContextMenu];
base::RecordAction(
base::UserMetricsAction(kDiscoverFeedUserActionCloseContextMenu));
}
- (void)recordOpenNativeBackOfCardMenu {
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kOpenedNativeActionSheet];
base::RecordAction(
base::UserMetricsAction(kDiscoverFeedUserActionNativeActionSheetOpened));
}
- (void)recordShowDialog {
[self
recordDiscoverFeedUserActionHistogram:FeedUserActionType::kOpenedDialog];
base::RecordAction(
base::UserMetricsAction(kDiscoverFeedUserActionReportContentOpened));
}
- (void)recordDismissDialog {
[self
recordDiscoverFeedUserActionHistogram:FeedUserActionType::kClosedDialog];
base::RecordAction(
base::UserMetricsAction(kDiscoverFeedUserActionReportContentClosed));
}
- (void)recordDismissCard {
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kEphemeralChange];
base::RecordAction(base::UserMetricsAction(kDiscoverFeedUserActionHideStory));
}
- (void)recordUndoDismissCard {
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kEphemeralChangeRejected];
}
- (void)recordCommittDismissCard {
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kEphemeralChangeCommited];
}
- (void)recordShowSnackbar {
[self
recordDiscoverFeedUserActionHistogram:FeedUserActionType::kShowSnackbar];
}
- (void)recordCommandID:(int)commandID {
base::UmaHistogramSparse(kDiscoverFeedUserActionCommandHistogram, commandID);
}
- (void)recordNoticeCardShown:(BOOL)shown {
base::UmaHistogramBoolean(kDiscoverFeedNoticeCardFulfilled, shown);
}
- (void)recordFeedArticlesFetchDurationInSeconds:
(NSTimeInterval)durationInSeconds
success:(BOOL)success {
if (success) {
UMA_HISTOGRAM_MEDIUM_TIMES(kDiscoverFeedArticlesFetchNetworkDurationSuccess,
base::Seconds(durationInSeconds));
} else {
UMA_HISTOGRAM_MEDIUM_TIMES(kDiscoverFeedArticlesFetchNetworkDurationFailure,
base::Seconds(durationInSeconds));
}
[self recordNetworkRequestDurationInSeconds:durationInSeconds];
}
- (void)recordFeedMoreArticlesFetchDurationInSeconds:
(NSTimeInterval)durationInSeconds
success:(BOOL)success {
if (success) {
UMA_HISTOGRAM_MEDIUM_TIMES(
kDiscoverFeedMoreArticlesFetchNetworkDurationSuccess,
base::Seconds(durationInSeconds));
} else {
UMA_HISTOGRAM_MEDIUM_TIMES(
kDiscoverFeedMoreArticlesFetchNetworkDurationFailure,
base::Seconds(durationInSeconds));
}
[self recordNetworkRequestDurationInSeconds:durationInSeconds];
}
- (void)recordFeedUploadActionsDurationInSeconds:
(NSTimeInterval)durationInSeconds
success:(BOOL)success {
if (success) {
UMA_HISTOGRAM_MEDIUM_TIMES(kDiscoverFeedUploadActionsNetworkDurationSuccess,
base::Seconds(durationInSeconds));
} else {
UMA_HISTOGRAM_MEDIUM_TIMES(kDiscoverFeedUploadActionsNetworkDurationFailure,
base::Seconds(durationInSeconds));
}
[self recordNetworkRequestDurationInSeconds:durationInSeconds];
}
- (void)recordNativeContextMenuVisibilityChanged:(BOOL)shown {
if (shown) {
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kOpenedNativeContextMenu];
base::RecordAction(base::UserMetricsAction(
kDiscoverFeedUserActionNativeContextMenuOpened));
} else {
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kClosedNativeContextMenu];
base::RecordAction(base::UserMetricsAction(
kDiscoverFeedUserActionNativeContextMenuClosed));
}
}
- (void)recordNativePulldownMenuVisibilityChanged:(BOOL)shown {
if (shown) {
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kOpenedNativePulldownMenu];
base::RecordAction(base::UserMetricsAction(
kDiscoverFeedUserActionNativePulldownMenuOpened));
} else {
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kClosedNativePulldownMenu];
base::RecordAction(base::UserMetricsAction(
kDiscoverFeedUserActionNativePulldownMenuClosed));
}
}
- (void)recordActivityLoggingEnabled:(BOOL)loggingEnabled {
base::UmaHistogramBoolean(kDiscoverFeedActivityLoggingEnabled,
loggingEnabled);
}
- (void)recordBrokenNTPHierarchy:(BrokenNTPHierarchyRelationship)relationship {
base::UmaHistogramEnumeration(kDiscoverFeedBrokenNTPHierarchy, relationship);
base::RecordAction(base::UserMetricsAction(kNTPViewHierarchyFixed));
}
- (void)recordFeedWillRefresh {
base::RecordAction(base::UserMetricsAction(kFeedWillRefresh));
}
#pragma mark - Private
// Records histogram metrics for Discover feed user actions.
- (void)recordDiscoverFeedUserActionHistogram:(FeedUserActionType)actionType {
UMA_HISTOGRAM_ENUMERATION(kDiscoverFeedUserActionHistogram, actionType);
[self recordInteraction];
}
// Records Feed engagement.
- (void)recordEngagement:(int)scrollDistance interacted:(BOOL)interacted {
scrollDistance = abs(scrollDistance);
// Determine if this interaction is part of a new 'session'.
base::Time now = base::Time::Now();
base::TimeDelta visitTimeout = base::Minutes(kMinutesBetweenSessions);
if (now - self.sessionStartTime > visitTimeout) {
[self finalizeSession];
}
// Reset the last active time for session measurement.
self.sessionStartTime = now;
// Report the user as engaged-simple if they have scrolled any amount or
// interacted with the card, and we have not already reported it for this
// chrome run.
if (!self.engagedSimpleReported && (scrollDistance > 0 || interacted)) {
[self recordEngagementTypeHistogram:FeedEngagementType::kFeedEngagedSimple];
self.engagedSimpleReported = YES;
}
// Report the user as engaged if they have scrolled more than the threshold or
// interacted with the card, and we have not already reported it this chrome
// run.
if (!self.engagedReported &&
(scrollDistance > kMinScrollThreshold || interacted)) {
[self recordEngagementTypeHistogram:FeedEngagementType::kFeedEngaged];
base::RecordAction(base::UserMetricsAction(kDiscoverFeedUserActionEngaged));
self.engagedReported = YES;
}
}
// Records any direct interaction with the Feed, this doesn't include scrolling.
- (void)recordInteraction {
[self recordEngagement:0 interacted:YES];
[self recordEngagementTypeHistogram:FeedEngagementType::kFeedInteracted];
}
// Records Engagement histograms of |engagementType|.
- (void)recordEngagementTypeHistogram:(FeedEngagementType)engagementType {
UMA_HISTOGRAM_ENUMERATION(kDiscoverFeedEngagementTypeHistogram,
engagementType);
}
// Resets the session tracking values, this occurs if there's been
// kMinutesBetweenSessions minutes between sessions.
- (void)finalizeSession {
if (!self.engagedSimpleReported)
return;
self.engagedReported = NO;
self.engagedSimpleReported = NO;
self.scrolledReported = NO;
}
// Records the |durationInSeconds| it took to Discover feed to perform any
// network operation.
- (void)recordNetworkRequestDurationInSeconds:
(NSTimeInterval)durationInSeconds {
UMA_HISTOGRAM_MEDIUM_TIMES(kDiscoverFeedNetworkDuration,
base::Seconds(durationInSeconds));
}
// Records that a URL was opened regardless of the target surface (e.g. New Tab,
// Same Tab, Incognito Tab, etc.)
- (void)recordOpenURL {
// TODO(crbug.com/1174088): Add card Index and the max number of suggestions.
UMA_HISTOGRAM_EXACT_LINEAR(kDiscoverFeedURLOpened, 0, 1);
}
@end