Commit a75f443f authored by sczs's avatar sczs Committed by Commit Bot

[ios] Adds engagement metrics recording to the Feed

- DiscoverFeedService is now in charge of creating and owning a single
instance of FeedMetricsRecorder.
- Add the FeedMetricsRecorder to DiscoverFeedConfig
- Adds engagement recording events

Bug: 1085419, 1127557
Change-Id: Iaf3cdcca850babb54bc8149050a4ebd6c3f8d68c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2406715
Commit-Queue: Sergio Collazos <sczs@chromium.org>
Reviewed-by: default avatarGanggui Tang <gogerald@chromium.org>
Cr-Commit-Position: refs/heads/master@{#806747}
parent 5b71def4
......@@ -18,6 +18,7 @@ source_set("discover_feed") {
"//components/signin/public/identity_manager",
"//ios/chrome/browser/browser_state",
"//ios/chrome/browser/signin",
"//ios/chrome/browser/ui/content_suggestions:metrics",
"//ios/public/provider/chrome/browser",
"//ios/public/provider/chrome/browser/discover_feed",
]
......
......@@ -9,6 +9,7 @@
#include "components/signin/public/identity_manager/identity_manager.h"
class ChromeBrowserState;
@class DiscoverFeedMetricsRecorder;
class DiscoverFeedProvider;
// A browser-context keyed service that is used to keep the Discover Feed data
......@@ -20,6 +21,10 @@ class DiscoverFeedService : public KeyedService,
DiscoverFeedService(ChromeBrowserState* browser_state);
~DiscoverFeedService() override;
// Returns the FeedMetricsRecorder to be used by the Feed, a single instance
// of DiscoverFeedMetricsRecorder needs to be used per BrowserState.
DiscoverFeedMetricsRecorder* GetDiscoverFeedMetricsRecorder();
// KeyedService:
void Shutdown() override;
......@@ -36,6 +41,9 @@ class DiscoverFeedService : public KeyedService,
// Discover Feed provider to notify of changes.
DiscoverFeedProvider* discover_feed_provider_;
// Metrics recorder for the DiscoverFeed.
DiscoverFeedMetricsRecorder* discover_feed_metrics_recorder_;
DISALLOW_COPY_AND_ASSIGN(DiscoverFeedService);
};
......
......@@ -7,6 +7,7 @@
#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
#import "ios/chrome/browser/signin/authentication_service_factory.h"
#include "ios/chrome/browser/signin/identity_manager_factory.h"
#import "ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h"
#import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
#import "ios/public/provider/chrome/browser/discover_feed/discover_feed_configuration.h"
#import "ios/public/provider/chrome/browser/discover_feed/discover_feed_provider.h"
......@@ -23,14 +24,22 @@ DiscoverFeedService::DiscoverFeedService(ChromeBrowserState* browser_state) {
identity_manager_->AddObserver(this);
}
discover_feed_metrics_recorder_ = [[DiscoverFeedMetricsRecorder alloc] init];
DiscoverFeedConfiguration* discover_config =
[[DiscoverFeedConfiguration alloc] init];
discover_config.browserState = browser_state;
discover_config.metricsRecorder = discover_feed_metrics_recorder_;
discover_feed_provider_->StartFeed(discover_config);
}
DiscoverFeedService::~DiscoverFeedService() {}
DiscoverFeedMetricsRecorder*
DiscoverFeedService::GetDiscoverFeedMetricsRecorder() {
return discover_feed_metrics_recorder_;
}
void DiscoverFeedService::Shutdown() {
if (identity_manager_) {
identity_manager_->RemoveObserver(this);
......
......@@ -23,8 +23,6 @@ source_set("content_suggestions") {
"content_suggestions_service_bridge_observer.h",
"content_suggestions_service_bridge_observer.mm",
"discover_feed_delegate.h",
"discover_feed_metrics_recorder.h",
"discover_feed_metrics_recorder.mm",
"mediator_util.h",
"mediator_util.mm",
"ntp_home_mediator.h",
......@@ -35,6 +33,7 @@ source_set("content_suggestions") {
]
deps = [
":feature_flags",
":metrics",
"//base",
"//components/favicon/core",
"//components/feature_engagement/public",
......@@ -103,6 +102,7 @@ source_set("content_suggestions") {
"//ui/base",
"//ui/strings",
]
public_deps = [ ":metrics" ]
configs += [ "//build/config/compiler:enable_arc" ]
}
......@@ -151,6 +151,7 @@ source_set("content_suggestions_ui") {
":content_suggestions_constant",
":content_suggestions_ui_util",
":feature_flags",
":metrics",
"resources:content_suggestions_no_image",
"resources:content_suggestions_offline",
"resources:ntp_search_icon",
......@@ -185,6 +186,15 @@ source_set("content_suggestions_ui") {
configs += [ "//build/config/compiler:enable_arc" ]
}
source_set("metrics") {
sources = [
"discover_feed_metrics_recorder.h",
"discover_feed_metrics_recorder.mm",
]
deps = [ "//base" ]
configs += [ "//build/config/compiler:enable_arc" ]
}
source_set("content_suggestions_constant") {
sources = [
"ntp_home_constant.h",
......
......@@ -19,6 +19,7 @@
#import "components/search_engines/template_url.h"
#import "components/search_engines/template_url_service.h"
#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
#include "ios/chrome/browser/discover_feed/discover_feed_service.h"
#include "ios/chrome/browser/discover_feed/discover_feed_service_factory.h"
#include "ios/chrome/browser/drag_and_drop/drag_and_drop_flag.h"
#import "ios/chrome/browser/drag_and_drop/url_drag_drop_handler.h"
......@@ -209,8 +210,11 @@
if (IsDiscoverFeedEnabled()) {
// Creating the DiscoverFeedService will start the DiscoverFeed.
DiscoverFeedServiceFactory::GetForBrowserState(
self.browser->GetBrowserState());
DiscoverFeedService* discoverFeedService =
DiscoverFeedServiceFactory::GetForBrowserState(
self.browser->GetBrowserState());
self.discoverFeedMetricsRecorder =
discoverFeedService->GetDiscoverFeedMetricsRecorder();
}
self.discoverFeedViewController = [self discoverFeed];
......@@ -239,7 +243,6 @@
self.metricsRecorder = [[ContentSuggestionsMetricsRecorder alloc] init];
self.metricsRecorder.delegate = self.contentSuggestionsMediator;
self.discoverFeedMetricsRecorder = [[DiscoverFeedMetricsRecorder alloc] init];
// Offset to maintain Discover feed scroll position.
CGFloat offset = 0;
......@@ -266,6 +269,8 @@
self.browser->GetCommandDispatcher(), SnackbarCommands);
self.suggestionsViewController.dispatcher = dispatcher;
self.suggestionsViewController.discoverFeedMenuHandler = self;
self.suggestionsViewController.discoverFeedMetricsRecorder =
self.discoverFeedMetricsRecorder;
self.discoverFeedHeaderDelegate =
self.suggestionsViewController.discoverFeedHeaderDelegate;
......
......@@ -21,6 +21,7 @@
@protocol ContentSuggestionsViewControllerAudience;
@protocol DiscoverFeedHeaderChanging;
@protocol DiscoverFeedMenuCommands;
@class DiscoverFeedMetricsRecorder;
@protocol OverscrollActionsControllerDelegate;
@protocol SnackbarCommands;
@protocol SuggestedContent;
......@@ -69,6 +70,9 @@ extern NSString* const
// Provider of menu configurations for the contentSuggestions component.
@property(nonatomic, weak) id<ContentSuggestionsMenuProvider> menuProvider
API_AVAILABLE(ios(13.0));
// Discover Feed metrics recorder.
@property(nonatomic, strong)
DiscoverFeedMetricsRecorder* discoverFeedMetricsRecorder;
- (void)setDataSource:(id<ContentSuggestionsDataSource>)dataSource;
- (void)setDispatcher:(id<SnackbarCommands>)dispatcher;
......
......@@ -28,6 +28,7 @@
#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recording.h"
#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_audience.h"
#import "ios/chrome/browser/ui/content_suggestions/discover_feed_menu_commands.h"
#import "ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h"
#import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
#import "ios/chrome/browser/ui/content_suggestions/theme_change_delegate.h"
#import "ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h"
......@@ -90,6 +91,9 @@ NSString* const kContentSuggestionsMostVisitedAccessibilityIdentifierPrefix =
// Whether this VC is observing the discoverFeedHeight using KVO or not.
@property(nonatomic, assign) BOOL observingDiscoverFeedHeight;
// The CollectionViewController scroll position when an scrolling event starts.
@property(nonatomic, assign) int scrollStartPosition;
@end
@implementation ContentSuggestionsViewController
......@@ -742,6 +746,7 @@ NSString* const kContentSuggestionsMostVisitedAccessibilityIdentifierPrefix =
- (void)scrollViewWillBeginDragging:(UIScrollView*)scrollView {
[self.overscrollActionsController scrollViewWillBeginDragging:scrollView];
self.scrollStartPosition = scrollView.contentOffset.y;
}
- (void)scrollViewDidEndDragging:(UIScrollView*)scrollView
......@@ -749,6 +754,8 @@ NSString* const kContentSuggestionsMostVisitedAccessibilityIdentifierPrefix =
[super scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
[self.overscrollActionsController scrollViewDidEndDragging:scrollView
willDecelerate:decelerate];
[self.discoverFeedMetricsRecorder
recordFeedScrolled:scrollView.contentOffset.y - self.scrollStartPosition];
}
- (void)scrollViewWillEndDragging:(UIScrollView*)scrollView
......
......@@ -10,6 +10,9 @@
// Records different metrics for the NTP's Discover feed.
@interface DiscoverFeedMetricsRecorder : NSObject
// Record metrics for when the user has scrolled |scrollDistance| in the Feed.
- (void)recordFeedScrolled:(int)scrollDistance;
// Record metrics for when the user has reached the bottom of their current
// feed.
- (void)recordInfiniteFeedTriggered;
......
......@@ -4,6 +4,7 @@
#import "ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h"
#import "base/mac/foundation_util.h"
#import "base/metrics/histogram_macros.h"
#import "base/metrics/user_metrics.h"
#import "base/metrics/user_metrics_action.h"
......@@ -78,6 +79,19 @@ enum class FeedUserActionType {
kMaxValue = kAddedToReadLater,
};
// 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 infinite feed trigger.
const char kDiscoverFeedInfiniteFeedTriggered[] =
......@@ -116,12 +130,48 @@ const char kDiscoverFeedUserActionManageInterestsTapped[] =
// User action name for infinite feed triggering.
const char kDiscoverFeedUserActionInfiniteFeedTriggered[] =
"ContentSuggestions.Feed.InfiniteFeedTriggered";
// 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;
} // namespace
@interface DiscoverFeedMetricsRecorder ()
// 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 DiscoverFeedMetricsRecorder
#pragma mark - Public
- (void)recordFeedScrolled:(int)scrollDistance {
[self recordEngagement:scrollDistance interacted:NO];
if (!self.scrolledReported) {
[self recordEngagementTypeHistogram:FeedEngagementType::kFeedScrolled];
self.scrolledReported = YES;
}
}
- (void)recordInfiniteFeedTriggered {
UMA_HISTOGRAM_ENUMERATION(kDiscoverFeedInfiniteFeedTriggered,
FeedLoadStreamStatus::kLoadedFromNetwork);
......@@ -202,6 +252,61 @@ const char kDiscoverFeedUserActionInfiniteFeedTriggered[] =
// 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::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 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;
}
@end
......@@ -8,6 +8,7 @@
#import <Foundation/Foundation.h>
class ChromeBrowserState;
@class DiscoverFeedMetricsRecorder;
// Configuration object used by the DiscoverFeedProvider.
@interface DiscoverFeedConfiguration : NSObject
......@@ -15,6 +16,9 @@ class ChromeBrowserState;
// BrowserState used by DiscoverFeedProvider;
@property(nonatomic, assign) ChromeBrowserState* browserState;
// DiscoverFeed metrics recorder used by DiscoverFeedProvider;
@property(nonatomic, strong) DiscoverFeedMetricsRecorder* metricsRecorder;
@end
#endif // IOS_PUBLIC_PROVIDER_CHROME_BROWSER_DISCOVER_FEED_DISCOVER_FEED_CONFIGURATION_H_
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