Skip to content

Commit

Permalink
[iOS] [WebChannels] Record follow count histogram and user action
Browse files Browse the repository at this point in the history
Records the number of followed publishers when:
1. Selecting Following feed and content is shown
2. Selecting Following feed and no content is shown
3. Following a channel
4. Unfollowing a channel
5. Engaging with the Following feed

Uses a new FollowDelegate so that the NTPCoordinator can make calls to
the FollowProvider on behalf of the FeedMetricsRecorder. Follow/Unfollow
channels will be logged in a separate CL.

Also logs user actions for each feed being selected from the header.

(cherry picked from commit fc35eb0)

Bug: 1322640
Change-Id: Idc696e46b9a743dda89e6277608612ee99051425
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3644971
Commit-Queue: Adam Arcaro <adamta@google.com>
Reviewed-by: Sergio Collazos <sczs@chromium.org>
Reviewed-by: Dan H <harringtond@chromium.org>
Reviewed-by: Justin DeWitt <dewittj@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#1004531}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3655701
Commit-Queue: Tina Wang <tinazwang@chromium.org>
Cr-Commit-Position: refs/branch-heads/5060@{#190}
Cr-Branched-From: b83393d-refs/heads/main@{#1002911}
  • Loading branch information
sdefresne authored and Chromium LUCI CQ committed May 23, 2022
1 parent 1cd2bda commit 3d84606
Show file tree
Hide file tree
Showing 10 changed files with 174 additions and 9 deletions.
2 changes: 2 additions & 0 deletions components/feed/core/v2/metrics_reporter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,8 @@ void MetricsReporter::OtherUserAction(const StreamType& stream_type,
case FeedUserActionType::kTappedDiscoverFeedPreview:
case FeedUserActionType::kOpenedAutoplaySettings:
case FeedUserActionType::kTappedFollowButton:
case FeedUserActionType::kDiscoverFeedSelected:
case FeedUserActionType::kFollowingFeedSelected:
// Nothing additional for these actions. Note that some of these are iOS
// only.

Expand Down
4 changes: 4 additions & 0 deletions components/feed/core/v2/public/common_enums.cc
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ std::ostream& operator<<(std::ostream& out, FeedUserActionType value) {
return out << "kTappedManageHidden";
case FeedUserActionType::kTappedFollowButton:
return out << "kTappedFollow";
case FeedUserActionType::kDiscoverFeedSelected:
return out << "kDiscoverFeedSelected";
case FeedUserActionType::kFollowingFeedSelected:
return out << "kFollowingFeedSelected";
}
}

Expand Down
6 changes: 5 additions & 1 deletion components/feed/core/v2/public/common_enums.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,12 @@ enum class FeedUserActionType {
kTappedManageHidden = 43,
// User tapped the "Follow" button on the main menu.
kTappedFollowButton = 44,
// User tapped on the Discover feed from the feed header.
kDiscoverFeedSelected = 45,
// User tapped on the Following feed from the feed header.
kFollowingFeedSelected = 46,

kMaxValue = kTappedFollowButton,
kMaxValue = kFollowingFeedSelected,
};

// For testing and debugging only.
Expand Down
1 change: 1 addition & 0 deletions ios/chrome/browser/ui/ntp/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ source_set("ntp") {
"new_tab_page_content_delegate.h",
"new_tab_page_controller_delegate.h",
"new_tab_page_delegate.h",
"new_tab_page_follow_delegate.h",
"new_tab_page_omnibox_positioning.h",
]
configs += [ "//build/config/compiler:enable_arc" ]
Expand Down
22 changes: 22 additions & 0 deletions ios/chrome/browser/ui/ntp/feed_metrics_recorder.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "ios/chrome/browser/discover_feed/feed_constants.h"

@protocol FeedControlDelegate;
@protocol NewTabPageFollowDelegate;

// DO NOT CHANGE. Values are from enums.xml representing what could be broken in
// the NTP view hierarchy. These values are persisted to logs. Entries should
Expand Down Expand Up @@ -61,6 +62,17 @@ enum class FollowSnackbarActionType {
kMaxValue = kSnackbarActionRetryUnfollow,
};

// Enum class for the times when we log the user's follow count.
// To be kept in sync with the ContentSuggestions.Feed.WebFeed.FollowCount
// variants.
typedef NS_ENUM(NSInteger, FollowCountLogReason) {
FollowCountLogReasonContentShown = 0,
FollowCountLogReasonNoContentShown,
FollowCountLogReasonAfterFollow,
FollowCountLogReasonAfterUnfollow,
FollowCountLogReasonEngaged
};

namespace base {
class Time;
}
Expand Down Expand Up @@ -203,6 +215,13 @@ class Time;
// Records that the feed is about to be refreshed.
- (void)recordFeedWillRefresh;

// Records that a given |feedType| was selected.
- (void)recordFeedSelected:(FeedType)feedType;

// Records the user's current follow count after a given event |logReason|.
- (void)recordFollowCount:(NSUInteger)followCount
forLogReason:(FollowCountLogReason)logReason;

// Records the state of the Feed setting based on the |enterprisePolicy| being
// enabled, |feedVisible|, the user being |signedIn|, user having |waaEnabled|
// and |spywEnabled|, and the |lastRefreshTime| for the Feed.
Expand Down Expand Up @@ -247,6 +266,9 @@ class Time;
// Delegate to get the currently selected feed.
@property(nonatomic, weak) id<FeedControlDelegate> feedControlDelegate;

// Delegate for getting information relating to Following.
@property(nonatomic, weak) id<NewTabPageFollowDelegate> followDelegate;

// Whether or not the feed is currently being shown on the Start Surface.
@property(nonatomic, assign) BOOL isShownOnStartSurface;

Expand Down
76 changes: 76 additions & 0 deletions ios/chrome/browser/ui/ntp/feed_metrics_recorder.mm
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#import "components/feed/core/v2/public/common_enums.h"
#import "ios/chrome/browser/ui/content_suggestions/ntp_home_metrics.h"
#import "ios/chrome/browser/ui/ntp/feed_control_delegate.h"
#import "ios/chrome/browser/ui/ntp/new_tab_page_follow_delegate.h"

#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
Expand Down Expand Up @@ -177,6 +178,13 @@
// User action indicating that the feed will refresh.
const char kFeedWillRefresh[] = "ContentSuggestions.Feed.WillRefresh";

// User action indicating that the Discover feed was selected.
const char kDiscoverFeedSelected[] = "ContentSuggestions.Feed.Selected";

// User action indicating that the Following feed was selected.
const char kFollowingFeedSelected[] =
"ContentSuggestions.Feed.WebFeed.Selected";

// 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.
Expand Down Expand Up @@ -262,6 +270,23 @@
const char kFeedUserSettingsOnStart[] =
"ContentSuggestions.Feed.UserSettingsOnStart";

// Histogram names for logging followed publisher count after certain events.
// After showing Following feed with content.
const char kFollowCountFollowingContentShown[] =
"ContentSuggestions.Feed.WebFeed.FollowCount.ContentShown";
// After showing Following feed without content.
const char kFollowCountFollowingNoContentShown[] =
"ContentSuggestions.Feed.WebFeed.FollowCount.NoContentShown";
// After following a channel.
const char kFollowCountAfterFollow[] =
"ContentSuggestions.Feed.WebFeed.FollowCount.AfterFollow";
// After unfollowing a channel.
const char kFollowCountAfterUnfollow[] =
"ContentSuggestions.Feed.WebFeed.FollowCount.AfterUnfollow";
// After engaging with the Following feed.
const char kFollowCountWhenEngaged[] =
"ContentSuggestions.Feed.WebFeed.FollowCount.Engaged";

// Minimum scrolling amount to record a FeedEngagementType::kFeedEngaged due to
// scrolling.
const int kMinScrollThreshold = 160;
Expand Down Expand Up @@ -606,6 +631,53 @@ - (void)recordFeedWillRefresh {
base::RecordAction(base::UserMetricsAction(kFeedWillRefresh));
}

- (void)recordFeedSelected:(FeedType)feedType {
DCHECK(self.followDelegate);
switch (feedType) {
case FeedTypeDiscover:
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kDiscoverFeedSelected];
base::RecordAction(base::UserMetricsAction(kDiscoverFeedSelected));
break;
case FeedTypeFollowing:
[self recordDiscoverFeedUserActionHistogram:FeedUserActionType::
kFollowingFeedSelected];
base::RecordAction(base::UserMetricsAction(kFollowingFeedSelected));
NSUInteger followCount = [self.followDelegate followedPublisherCount];
if (followCount > 0 &&
[self.followDelegate doesFollowingFeedHaveContent]) {
[self recordFollowCount:followCount
forLogReason:FollowCountLogReasonContentShown];
} else {
[self recordFollowCount:followCount
forLogReason:FollowCountLogReasonNoContentShown];
}
break;
}
}

- (void)recordFollowCount:(NSUInteger)followCount
forLogReason:(FollowCountLogReason)logReason {
switch (logReason) {
case FollowCountLogReasonContentShown:
base::UmaHistogramSparse(kFollowCountFollowingContentShown, followCount);
break;
case FollowCountLogReasonNoContentShown:
base::UmaHistogramSparse(kFollowCountFollowingNoContentShown,
followCount);
break;
case FollowCountLogReasonAfterFollow:
base::UmaHistogramSparse(kFollowCountAfterFollow, followCount);
break;
case FollowCountLogReasonAfterUnfollow:
base::UmaHistogramSparse(kFollowCountAfterUnfollow, followCount);
break;
case FollowCountLogReasonEngaged:
base::UmaHistogramSparse(kFollowCountWhenEngaged, followCount);
break;
}
}

- (void)recordFeedSettingsOnStartForEnterprisePolicy:(BOOL)enterprisePolicy
feedVisible:(BOOL)feedVisible
signedIn:(BOOL)signedIn
Expand Down Expand Up @@ -830,6 +902,10 @@ - (void)recordEngaged {
UMA_HISTOGRAM_ENUMERATION(kFollowingFeedEngagementTypeHistogram,
FeedEngagementType::kFeedEngaged);
self.engagedReportedFollowing = YES;

// Log follow count when engaging with Following feed.
[self recordFollowCount:[self.followDelegate followedPublisherCount]
forLogReason:FollowCountLogReasonEngaged];
}

// TODO(crbug.com/1322640): Separate user action for Following feed
Expand Down
33 changes: 25 additions & 8 deletions ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
#import "ios/chrome/browser/ui/ntp/new_tab_page_content_delegate.h"
#import "ios/chrome/browser/ui/ntp/new_tab_page_delegate.h"
#import "ios/chrome/browser/ui/ntp/new_tab_page_feature.h"
#import "ios/chrome/browser/ui/ntp/new_tab_page_follow_delegate.h"
#import "ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h"
#import "ios/chrome/browser/ui/ntp/notification_promo_whats_new.h"
#import "ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.h"
Expand Down Expand Up @@ -103,6 +104,7 @@ @interface NewTabPageCoordinator () <AppStateObserver,
FeedMenuCommands,
NewTabPageContentDelegate,
NewTabPageDelegate,
NewTabPageFollowDelegate,
OverscrollActionsControllerDelegate,
PrefObserverDelegate,
SceneStateObserver> {
Expand Down Expand Up @@ -278,6 +280,7 @@ - (void)start {

self.feedMetricsRecorder = self.discoverFeedService->GetFeedMetricsRecorder();
self.feedMetricsRecorder.feedControlDelegate = self;
self.feedMetricsRecorder.followDelegate = self;

if (IsContentSuggestionsHeaderMigrationEnabled()) {
self.headerController =
Expand Down Expand Up @@ -581,9 +584,7 @@ - (void)constrainDiscoverHeaderMenuButtonNamedGuide {

- (void)updateFollowingFeedHasUnseenContent:(BOOL)hasUnseenContent {
DCHECK(IsWebChannelsEnabled());
if (ios::GetChromeBrowserProvider()
.GetFollowProvider()
->DoesFollowingFeedHaveContent()) {
if ([self doesFollowingFeedHaveContent]) {
[self.feedHeaderViewController
updateFollowingSegmentDotForUnseenContent:hasUnseenContent];
}
Expand All @@ -601,6 +602,7 @@ - (void)ntpDidChangeVisibility:(BOOL)visible {
(FollowingFeedSortType)self.prefService->GetInteger(
prefs::kNTPFollowingFeedSortType);
self.feedMetricsRecorder.feedControlDelegate = self;
self.feedMetricsRecorder.followDelegate = self;
}
}
}
Expand All @@ -616,13 +618,16 @@ - (void)handleFeedSelected:(FeedType)feedType {
// Saves scroll position before changing feed.
CGFloat scrollPosition = [self.ntpViewController scrollPosition];

[self.feedMetricsRecorder recordFeedSelected:feedType];

if (feedType == FeedTypeFollowing) {
// Clears dot and notifies service that the Following feed content has been
// seen.
// Clears dot and notifies service that the Following feed content has
// been seen.
[self.feedHeaderViewController
updateFollowingSegmentDotForUnseenContent:NO];
self.discoverFeedService->SetFollowingFeedContentSeen();
}

self.selectedFeed = feedType;
[self updateNTPForFeed];
[self updateFeedLayout];
Expand All @@ -644,6 +649,20 @@ - (BOOL)shouldFeedBeVisible {
!IsFeedAblationEnabled();
}

#pragma mark - NewTabPageFollowDelegate

- (NSUInteger)followedPublisherCount {
return [ios::GetChromeBrowserProvider()
.GetFollowProvider()
->GetFollowedWebChannels() count];
}

- (BOOL)doesFollowingFeedHaveContent {
return ios::GetChromeBrowserProvider()
.GetFollowProvider()
->DoesFollowingFeedHaveContent();
}

#pragma mark - FeedMenuCommands

- (void)openFeedMenu {
Expand Down Expand Up @@ -1170,9 +1189,7 @@ - (FeedHeaderViewController*)feedHeaderViewController {
if (!_feedHeaderViewController) {
// Only show the dot if the user follows available publishers.
BOOL followingSegmentDotVisible =
ios::GetChromeBrowserProvider()
.GetFollowProvider()
->DoesFollowingFeedHaveContent() &&
[self doesFollowingFeedHaveContent] &&
self.discoverFeedService->GetFollowingFeedHasUnseenContent();
_feedHeaderViewController = [[FeedHeaderViewController alloc]
initWithFollowingFeedSortType:(FollowingFeedSortType)
Expand Down
19 changes: 19 additions & 0 deletions ios/chrome/browser/ui/ntp/new_tab_page_follow_delegate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright 2022 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.

#ifndef IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_FOLLOW_DELEGATE_H_
#define IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_FOLLOW_DELEGATE_H_

// Delegate for getting information relating to Following.
@protocol NewTabPageFollowDelegate

// Returns the number of publishers the user follows.
- (NSUInteger)followedPublisherCount;

// Returns whether the user has content in their Following feed.
- (BOOL)doesFollowingFeedHaveContent;

@end

#endif // IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_FOLLOW_DELEGATE_H_
16 changes: 16 additions & 0 deletions tools/metrics/actions/actions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6250,6 +6250,22 @@ should be able to be added at any place in this file.
</description>
</action>

<action name="ContentSuggestions.Feed.Selected">
<owner>adamta@google.com</owner>
<owner>sczs@chromium.org</owner>
<owner>feed@chromium.org</owner>
<description>The user has selected the Discover feed in the NTP.</description>
</action>

<action name="ContentSuggestions.Feed.WebFeed.Selected">
<owner>adamta@google.com</owner>
<owner>sczs@chromium.org</owner>
<owner>feed@chromium.org</owner>
<description>
The user has selected the Following feed in the NTP.
</description>
</action>

<action name="ContentSuggestions.Feed.WillRefresh">
<owner>adamta@google.org</owner>
<owner>sczs@chromium.org</owner>
Expand Down
4 changes: 4 additions & 0 deletions tools/metrics/histograms/enums.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40616,6 +40616,10 @@ Called by update_permissions_policy_enum.py.-->
<int value="43"
label="User tapped &quot;Hidden&quot; in the feed management
interstitial."/>
<int value="45"
label="User selected the Discover feed from the feed header."/>
<int value="46"
label="User selected the Following feed from the feed header."/>
</enum>

<enum name="FeedUserCommandType">
Expand Down

0 comments on commit 3d84606

Please sign in to comment.