Commit a411dd26 authored by adamta's avatar adamta Committed by Chromium LUCI CQ

[iOS] Record engagement and interactions for Zine feed

Adds parent metrics recorder for common feed metrics shared across
Zine and Discover. Allows us to have the same engagement metrics for
both Zine and Discover feeds, to compare Apfelstrudel engagement and
interactions.

Change-Id: Ib94e459b4cd0339d2c066570fbd21379dbffe32b
Bug: 1155224
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2570555
Commit-Queue: Adam Trudeau-Arcaro <adamta@google.com>
Reviewed-by: default avatarSergio Collazos <sczs@chromium.org>
Cr-Commit-Position: refs/heads/master@{#833411}
parent 2052bd1d
...@@ -193,6 +193,7 @@ source_set("metrics") { ...@@ -193,6 +193,7 @@ source_set("metrics") {
"discover_feed_metrics_recorder.mm", "discover_feed_metrics_recorder.mm",
] ]
deps = [ deps = [
":feature_flags",
"//base", "//base",
"//components/feed/core/v2:common", "//components/feed/core/v2:common",
] ]
......
...@@ -5,10 +5,12 @@ ...@@ -5,10 +5,12 @@
#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recorder.h" #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recorder.h"
#include "base/mac/foundation_util.h" #include "base/mac/foundation_util.h"
#import "base/metrics/histogram_macros.h"
#include "components/ntp_snippets/content_suggestions_metrics.h" #include "components/ntp_snippets/content_suggestions_metrics.h"
#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.h" #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.h"
#import "ios/chrome/browser/ui/content_suggestions/cells/suggested_content.h" #import "ios/chrome/browser/ui/content_suggestions/cells/suggested_content.h"
#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_category_wrapper.h" #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_category_wrapper.h"
#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_feature.h"
#import "ios/chrome/browser/ui/content_suggestions/identifier/content_suggestion_identifier.h" #import "ios/chrome/browser/ui/content_suggestions/identifier/content_suggestion_identifier.h"
#import "ios/chrome/browser/ui/content_suggestions/identifier/content_suggestions_section_information.h" #import "ios/chrome/browser/ui/content_suggestions/identifier/content_suggestions_section_information.h"
...@@ -16,6 +18,48 @@ ...@@ -16,6 +18,48 @@
#error "This file requires ARC support." #error "This file requires ARC support."
#endif #endif
// Values for the UMA ContentSuggestions.Feed.EngagementType
// 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 FeedEngagementType in enums.xml.
enum class FeedEngagementType {
kFeedEngaged = 0,
kFeedEngagedSimple = 1,
kFeedInteracted = 2,
kDeprecatedFeedScrolled = 3,
kFeedScrolled = 4,
kMaxValue = kFeedScrolled,
};
namespace {
// Histogram name for the feed engagement types.
const char kDiscoverFeedEngagementTypeHistogram[] =
"ContentSuggestions.Feed.EngagementType";
// 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;
}
@interface ContentSuggestionsMetricsRecorder ()
// 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 ContentSuggestionsMetricsRecorder @implementation ContentSuggestionsMetricsRecorder
@synthesize delegate = _delegate; @synthesize delegate = _delegate;
...@@ -36,6 +80,7 @@ ...@@ -36,6 +80,7 @@
suggestionsAbove + indexPath.item, [categoryWrapper category], suggestionsAbove + indexPath.item, [categoryWrapper category],
sectionsShownAbove, indexPath.item, item.publishDate, item.score, action, sectionsShownAbove, indexPath.item, item.publishDate, item.score, action,
/*is_prefetched=*/false, /*is_offline=*/false); /*is_prefetched=*/false, /*is_offline=*/false);
[self recordInteraction];
} }
- (void)onMenuOpenedForSuggestion:(ContentSuggestionsItem*)item - (void)onMenuOpenedForSuggestion:(ContentSuggestionsItem*)item
...@@ -49,6 +94,7 @@ ...@@ -49,6 +94,7 @@
ntp_snippets::metrics::OnSuggestionMenuOpened( ntp_snippets::metrics::OnSuggestionMenuOpened(
suggestionsAbove + indexPath.item, [categoryWrapper category], suggestionsAbove + indexPath.item, [categoryWrapper category],
indexPath.item, item.publishDate, item.score); indexPath.item, item.publishDate, item.score);
[self recordInteraction];
} }
#pragma mark - ContentSuggestionsMetricsRecording #pragma mark - ContentSuggestionsMetricsRecording
...@@ -77,6 +123,7 @@ ...@@ -77,6 +123,7 @@
ntp_snippets::metrics::OnMoreButtonClicked([categoryWrapper category], ntp_snippets::metrics::OnMoreButtonClicked([categoryWrapper category],
position); position);
[self recordInteraction];
} }
- (void)onSuggestionDismissed:(CollectionViewItem<SuggestedContent>*)item - (void)onSuggestionDismissed:(CollectionViewItem<SuggestedContent>*)item
...@@ -90,6 +137,74 @@ ...@@ -90,6 +137,74 @@
ntp_snippets::metrics::OnSuggestionDismissed( ntp_snippets::metrics::OnSuggestionDismissed(
suggestionsAbove + indexPath.item, [categoryWrapper category], suggestionsAbove + indexPath.item, [categoryWrapper category],
indexPath.item, /*visited=*/false); indexPath.item, /*visited=*/false);
[self recordInteraction];
}
- (void)recordFeedScrolled:(int)scrollDistance {
DCHECK(!IsDiscoverFeedEnabled());
[self recordEngagement:scrollDistance interacted:NO];
if (!self.scrolledReported) {
[self recordEngagementTypeHistogram:FeedEngagementType::kFeedScrolled];
self.scrolledReported = YES;
}
}
#pragma mark - Private
// 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::TimeDelta::FromMinutes(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];
self.engagedReported = YES;
}
}
// Records Engagement histograms of |engagementType|.
- (void)recordEngagementTypeHistogram:(FeedEngagementType)engagementType {
UMA_HISTOGRAM_ENUMERATION(kDiscoverFeedEngagementTypeHistogram,
engagementType);
}
// Records any direct interaction with the Feed, this doesn't include scrolling.
- (void)recordInteraction {
DCHECK(!IsDiscoverFeedEnabled());
[self recordEngagement:0 interacted:YES];
[self recordEngagementTypeHistogram:FeedEngagementType::kFeedInteracted];
}
// 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;
} }
@end @end
...@@ -34,6 +34,9 @@ ...@@ -34,6 +34,9 @@
atIndexPath:(NSIndexPath*)indexPath atIndexPath:(NSIndexPath*)indexPath
suggestionsShownAbove:(NSInteger)suggestionsAbove; suggestionsShownAbove:(NSInteger)suggestionsAbove;
// Record metrics for when the user has scrolled |scrollDistance| in the Feed.
- (void)recordFeedScrolled:(int)scrollDistance;
@end @end
#endif // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_METRICS_RECORDING_H_ #endif // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_METRICS_RECORDING_H_
...@@ -750,8 +750,14 @@ NSString* const kContentSuggestionsMostVisitedAccessibilityIdentifierPrefix = ...@@ -750,8 +750,14 @@ NSString* const kContentSuggestionsMostVisitedAccessibilityIdentifierPrefix =
willDecelerate:decelerate]; willDecelerate:decelerate];
[self.panGestureHandler scrollViewDidEndDragging:scrollView [self.panGestureHandler scrollViewDidEndDragging:scrollView
willDecelerate:decelerate]; willDecelerate:decelerate];
[self.discoverFeedMetricsRecorder if (IsDiscoverFeedEnabled()) {
recordFeedScrolled:scrollView.contentOffset.y - self.scrollStartPosition]; [self.discoverFeedMetricsRecorder
recordFeedScrolled:scrollView.contentOffset.y -
self.scrollStartPosition];
} else {
[self.metricsRecorder recordFeedScrolled:scrollView.contentOffset.y -
self.scrollStartPosition];
}
} }
- (void)scrollViewWillEndDragging:(UIScrollView*)scrollView - (void)scrollViewWillEndDragging:(UIScrollView*)scrollView
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#import "base/metrics/user_metrics.h" #import "base/metrics/user_metrics.h"
#import "base/metrics/user_metrics_action.h" #import "base/metrics/user_metrics_action.h"
#import "components/feed/core/v2/common_enums.h" #import "components/feed/core/v2/common_enums.h"
#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_feature.h"
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support." #error "This file requires ARC support."
...@@ -144,6 +145,7 @@ const int kMinutesBetweenSessions = 5; ...@@ -144,6 +145,7 @@ const int kMinutesBetweenSessions = 5;
#pragma mark - Public #pragma mark - Public
- (void)recordFeedScrolled:(int)scrollDistance { - (void)recordFeedScrolled:(int)scrollDistance {
DCHECK(IsDiscoverFeedEnabled());
[self recordEngagement:scrollDistance interacted:NO]; [self recordEngagement:scrollDistance interacted:NO];
if (!self.scrolledReported) { if (!self.scrolledReported) {
...@@ -339,6 +341,7 @@ const int kMinutesBetweenSessions = 5; ...@@ -339,6 +341,7 @@ const int kMinutesBetweenSessions = 5;
// Records any direct interaction with the Feed, this doesn't include scrolling. // Records any direct interaction with the Feed, this doesn't include scrolling.
- (void)recordInteraction { - (void)recordInteraction {
DCHECK(IsDiscoverFeedEnabled());
[self recordEngagement:0 interacted:YES]; [self recordEngagement:0 interacted:YES];
[self recordEngagementTypeHistogram:FeedEngagementType::kFeedInteracted]; [self recordEngagementTypeHistogram:FeedEngagementType::kFeedInteracted];
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment