Commit 82320bd1 authored by Gang Wu's avatar Gang Wu Committed by Commit Bot

[Feed] Add user action for the Feed

This CL for Recording user actions in Feed, which were recorded in Zine.

Bug: 902126
Change-Id: Ie20f82d5644dd856f350c4ad7729b72946a41601
Reviewed-on: https://chromium-review.googlesource.com/c/1330721
Commit-Queue: Gang Wu <gangwu@chromium.org>
Reviewed-by: default avatarFilip Gorski <fgorski@chromium.org>
Cr-Commit-Position: refs/heads/master@{#607520}
parent 53a1b6f6
...@@ -9,6 +9,7 @@ import com.google.android.libraries.feed.host.logging.BasicLoggingApi; ...@@ -9,6 +9,7 @@ import com.google.android.libraries.feed.host.logging.BasicLoggingApi;
import com.google.android.libraries.feed.host.logging.ContentLoggingData; import com.google.android.libraries.feed.host.logging.ContentLoggingData;
import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.JNINamespace;
import org.chromium.chrome.browser.ntp.NewTabPageUma;
import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.ui.mojom.WindowOpenDisposition; import org.chromium.ui.mojom.WindowOpenDisposition;
...@@ -64,6 +65,7 @@ public class FeedLoggingBridge implements BasicLoggingApi { ...@@ -64,6 +65,7 @@ public class FeedLoggingBridge implements BasicLoggingApi {
@Override @Override
public void onClientAction(ContentLoggingData data, @ActionType int actionType) { public void onClientAction(ContentLoggingData data, @ActionType int actionType) {
assert mNativeFeedLoggingBridge != 0; assert mNativeFeedLoggingBridge != 0;
recordUserAction(actionType);
nativeOnClientAction( nativeOnClientAction(
mNativeFeedLoggingBridge, feedActionToWindowOpenDisposition(actionType)); mNativeFeedLoggingBridge, feedActionToWindowOpenDisposition(actionType));
} }
...@@ -110,13 +112,15 @@ public class FeedLoggingBridge implements BasicLoggingApi { ...@@ -110,13 +112,15 @@ public class FeedLoggingBridge implements BasicLoggingApi {
* *
* @param visitTimeMs Time spent reading the page. * @param visitTimeMs Time spent reading the page.
* @param isOffline If the page is viewed in offline mode or not. * @param isOffline If the page is viewed in offline mode or not.
* @param returnToNtp User backed to NTP after visit the page.
*/ */
public void onContentTargetVisited(long visitTimeMs, boolean isOffline) { public void onContentTargetVisited(long visitTimeMs, boolean isOffline, boolean returnToNtp) {
// We cannot assume that the|mNativeFeedLoggingBridge| is always available like other // We cannot assume that the|mNativeFeedLoggingBridge| is always available like other
// methods. This method is called by objects not controlled by Feed lifetimes, and destroy() // methods. This method is called by objects not controlled by Feed lifetimes, and destroy()
// may have already been called if Feed is disabled by policy. // may have already been called if Feed is disabled by policy.
if (mNativeFeedLoggingBridge != 0) { if (mNativeFeedLoggingBridge != 0) {
nativeOnContentTargetVisited(mNativeFeedLoggingBridge, visitTimeMs, isOffline); nativeOnContentTargetVisited(
mNativeFeedLoggingBridge, visitTimeMs, isOffline, returnToNtp);
} }
} }
...@@ -139,6 +143,24 @@ public class FeedLoggingBridge implements BasicLoggingApi { ...@@ -139,6 +143,24 @@ public class FeedLoggingBridge implements BasicLoggingApi {
} }
} }
private void recordUserAction(@ActionType int actionType) {
switch (actionType) {
case ActionType.OPEN_URL:
case ActionType.OPEN_URL_INCOGNITO:
case ActionType.OPEN_URL_NEW_TAB:
case ActionType.OPEN_URL_NEW_WINDOW:
NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_SNIPPET);
break;
case ActionType.LEARN_MORE:
NewTabPageUma.recordAction(NewTabPageUma.ACTION_CLICKED_LEARN_MORE);
break;
case ActionType.DOWNLOAD:
case ActionType.UNKNOWN:
default:
break;
}
}
private native long nativeInit(Profile profile); private native long nativeInit(Profile profile);
private native void nativeDestroy(long nativeFeedLoggingBridge); private native void nativeDestroy(long nativeFeedLoggingBridge);
private native void nativeOnContentViewed(long nativeFeedLoggingBridge, int position, private native void nativeOnContentViewed(long nativeFeedLoggingBridge, int position,
...@@ -158,5 +180,5 @@ public class FeedLoggingBridge implements BasicLoggingApi { ...@@ -158,5 +180,5 @@ public class FeedLoggingBridge implements BasicLoggingApi {
private native void nativeOnOpenedWithNoImmediateContent(long nativeFeedLoggingBridge); private native void nativeOnOpenedWithNoImmediateContent(long nativeFeedLoggingBridge);
private native void nativeOnOpenedWithNoContent(long nativeFeedLoggingBridge); private native void nativeOnOpenedWithNoContent(long nativeFeedLoggingBridge);
private native void nativeOnContentTargetVisited( private native void nativeOnContentTargetVisited(
long nativeFeedLoggingBridge, long visitTimeMs, boolean isOffline); long nativeFeedLoggingBridge, long visitTimeMs, boolean isOffline, boolean returnToNtp);
} }
...@@ -12,6 +12,7 @@ import com.google.android.libraries.feed.host.action.ActionApi; ...@@ -12,6 +12,7 @@ import com.google.android.libraries.feed.host.action.ActionApi;
import org.chromium.chrome.browser.ChromeFeatureList; import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.feed.FeedLoggingBridge; import org.chromium.chrome.browser.feed.FeedLoggingBridge;
import org.chromium.chrome.browser.feed.FeedOfflineIndicator; import org.chromium.chrome.browser.feed.FeedOfflineIndicator;
import org.chromium.chrome.browser.ntp.NewTabPage;
import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
import org.chromium.chrome.browser.suggestions.NavigationRecorder; import org.chromium.chrome.browser.suggestions.NavigationRecorder;
import org.chromium.chrome.browser.suggestions.SuggestionsConfig; import org.chromium.chrome.browser.suggestions.SuggestionsConfig;
...@@ -167,10 +168,12 @@ public class FeedActionHandler implements ActionApi { ...@@ -167,10 +168,12 @@ public class FeedActionHandler implements ActionApi {
private void openAndRecord(int disposition, LoadUrlParams loadUrlParams, boolean isOffline) { private void openAndRecord(int disposition, LoadUrlParams loadUrlParams, boolean isOffline) {
Tab loadingTab = mDelegate.openUrl(disposition, loadUrlParams); Tab loadingTab = mDelegate.openUrl(disposition, loadUrlParams);
if (loadingTab != null) { if (loadingTab != null) {
// Records how long the user spending on the suggested page. // Records how long the user spending on the suggested page, and whether the user got
// back to the NTP.
NavigationRecorder.record(loadingTab, NavigationRecorder.record(loadingTab,
visitData visitData
-> mLoggingBridge.onContentTargetVisited(visitData.duration, isOffline)); -> mLoggingBridge.onContentTargetVisited(
visitData.duration, isOffline, NewTabPage.isNTPUrl(visitData.endUrl)));
} }
mSuggestionConsumedObserver.run(); mSuggestionConsumedObserver.run();
} }
......
...@@ -163,7 +163,8 @@ public class FeedActionHandlerTest { ...@@ -163,7 +163,8 @@ public class FeedActionHandlerTest {
answerWithGoodParams(); answerWithGoodParams();
mActionHandler.openUrl(TEST_URL); mActionHandler.openUrl(TEST_URL);
verifyOpenedOffline(WindowOpenDisposition.CURRENT_TAB); verifyOpenedOffline(WindowOpenDisposition.CURRENT_TAB);
verify(mLoggingBridge, times(1)).onContentTargetVisited(anyLong(), /*isOffline*/ eq(true)); verify(mLoggingBridge, times(1))
.onContentTargetVisited(anyLong(), /*isOffline*/ eq(true), anyBoolean());
} }
@Test @Test
...@@ -172,7 +173,8 @@ public class FeedActionHandlerTest { ...@@ -172,7 +173,8 @@ public class FeedActionHandlerTest {
when(mOfflineIndicator.getOfflineIdIfPageIsOfflined(TEST_URL)).thenReturn(null); when(mOfflineIndicator.getOfflineIdIfPageIsOfflined(TEST_URL)).thenReturn(null);
mActionHandler.openUrl(TEST_URL); mActionHandler.openUrl(TEST_URL);
verifyOpenedOnline(WindowOpenDisposition.CURRENT_TAB); verifyOpenedOnline(WindowOpenDisposition.CURRENT_TAB);
verify(mLoggingBridge, times(1)).onContentTargetVisited(anyLong(), /*isOffline*/ eq(false)); verify(mLoggingBridge, times(1))
.onContentTargetVisited(anyLong(), /*isOffline*/ eq(false), anyBoolean());
} }
@Test @Test
...@@ -183,7 +185,8 @@ public class FeedActionHandlerTest { ...@@ -183,7 +185,8 @@ public class FeedActionHandlerTest {
answerWithGivenParams(null); answerWithGivenParams(null);
mActionHandler.openUrl(TEST_URL); mActionHandler.openUrl(TEST_URL);
verifyOpenedOnline(WindowOpenDisposition.CURRENT_TAB); verifyOpenedOnline(WindowOpenDisposition.CURRENT_TAB);
verify(mLoggingBridge, times(1)).onContentTargetVisited(anyLong(), /*isOffline*/ eq(false)); verify(mLoggingBridge, times(1))
.onContentTargetVisited(anyLong(), /*isOffline*/ eq(false), anyBoolean());
} }
@Test @Test
...@@ -204,7 +207,8 @@ public class FeedActionHandlerTest { ...@@ -204,7 +207,8 @@ public class FeedActionHandlerTest {
answerWithGoodParams(); answerWithGoodParams();
mActionHandler.openUrlInNewTab(TEST_URL); mActionHandler.openUrlInNewTab(TEST_URL);
verifyOpenedOffline(WindowOpenDisposition.NEW_BACKGROUND_TAB); verifyOpenedOffline(WindowOpenDisposition.NEW_BACKGROUND_TAB);
verify(mLoggingBridge, times(1)).onContentTargetVisited(anyLong(), /*isOffline*/ eq(true)); verify(mLoggingBridge, times(1))
.onContentTargetVisited(anyLong(), /*isOffline*/ eq(true), anyBoolean());
} }
@Test @Test
...@@ -213,7 +217,8 @@ public class FeedActionHandlerTest { ...@@ -213,7 +217,8 @@ public class FeedActionHandlerTest {
when(mOfflineIndicator.getOfflineIdIfPageIsOfflined(TEST_URL)).thenReturn(null); when(mOfflineIndicator.getOfflineIdIfPageIsOfflined(TEST_URL)).thenReturn(null);
mActionHandler.openUrlInNewTab(TEST_URL); mActionHandler.openUrlInNewTab(TEST_URL);
verifyOpenedOnline(WindowOpenDisposition.NEW_BACKGROUND_TAB); verifyOpenedOnline(WindowOpenDisposition.NEW_BACKGROUND_TAB);
verify(mLoggingBridge, times(1)).onContentTargetVisited(anyLong(), /*isOffline*/ eq(false)); verify(mLoggingBridge, times(1))
.onContentTargetVisited(anyLong(), /*isOffline*/ eq(false), anyBoolean());
} }
@Test @Test
...@@ -223,7 +228,8 @@ public class FeedActionHandlerTest { ...@@ -223,7 +228,8 @@ public class FeedActionHandlerTest {
answerWithGoodParams(); answerWithGoodParams();
mActionHandler.openUrlInNewWindow(TEST_URL); mActionHandler.openUrlInNewWindow(TEST_URL);
verifyOpenedOffline(WindowOpenDisposition.NEW_WINDOW); verifyOpenedOffline(WindowOpenDisposition.NEW_WINDOW);
verify(mLoggingBridge, times(1)).onContentTargetVisited(anyLong(), /*isOffline*/ eq(true)); verify(mLoggingBridge, times(1))
.onContentTargetVisited(anyLong(), /*isOffline*/ eq(true), anyBoolean());
} }
@Test @Test
...@@ -232,7 +238,8 @@ public class FeedActionHandlerTest { ...@@ -232,7 +238,8 @@ public class FeedActionHandlerTest {
when(mOfflineIndicator.getOfflineIdIfPageIsOfflined(TEST_URL)).thenReturn(null); when(mOfflineIndicator.getOfflineIdIfPageIsOfflined(TEST_URL)).thenReturn(null);
mActionHandler.openUrlInNewWindow(TEST_URL); mActionHandler.openUrlInNewWindow(TEST_URL);
verifyOpenedOnline(WindowOpenDisposition.NEW_WINDOW); verifyOpenedOnline(WindowOpenDisposition.NEW_WINDOW);
verify(mLoggingBridge, times(1)).onContentTargetVisited(anyLong(), /*isOffline*/ eq(false)); verify(mLoggingBridge, times(1))
.onContentTargetVisited(anyLong(), /*isOffline*/ eq(false), anyBoolean());
} }
@Test @Test
...@@ -257,6 +264,7 @@ public class FeedActionHandlerTest { ...@@ -257,6 +264,7 @@ public class FeedActionHandlerTest {
when(mOfflineIndicator.getOfflineIdIfPageIsOfflined(TEST_URL)).thenReturn(null); when(mOfflineIndicator.getOfflineIdIfPageIsOfflined(TEST_URL)).thenReturn(null);
mActionHandler.openUrl(TEST_URL); mActionHandler.openUrl(TEST_URL);
verifyOpenedOnline(WindowOpenDisposition.CURRENT_TAB); verifyOpenedOnline(WindowOpenDisposition.CURRENT_TAB);
verify(mLoggingBridge, times(0)).onContentTargetVisited(anyLong(), anyBoolean()); verify(mLoggingBridge, times(0))
.onContentTargetVisited(anyLong(), anyBoolean(), anyBoolean());
} }
} }
...@@ -124,13 +124,14 @@ void FeedLoggingBridge::OnContentTargetVisited( ...@@ -124,13 +124,14 @@ void FeedLoggingBridge::OnContentTargetVisited(
JNIEnv* j_env, JNIEnv* j_env,
const base::android::JavaRef<jobject>& j_this, const base::android::JavaRef<jobject>& j_this,
const jlong visit_time_ms, const jlong visit_time_ms,
const jboolean is_offline) { const jboolean is_offline,
const jboolean return_to_ntp) {
if (is_offline) { if (is_offline) {
feed_logging_metrics_->OnSuggestionOfflinePageVisited( feed_logging_metrics_->OnSuggestionOfflinePageVisited(
base::TimeDelta::FromMilliseconds(visit_time_ms)); base::TimeDelta::FromMilliseconds(visit_time_ms), return_to_ntp);
} else { } else {
feed_logging_metrics_->OnSuggestionArticleVisited( feed_logging_metrics_->OnSuggestionArticleVisited(
base::TimeDelta::FromMilliseconds(visit_time_ms)); base::TimeDelta::FromMilliseconds(visit_time_ms), return_to_ntp);
} }
} }
......
...@@ -74,7 +74,8 @@ class FeedLoggingBridge { ...@@ -74,7 +74,8 @@ class FeedLoggingBridge {
void OnContentTargetVisited(JNIEnv* j_env, void OnContentTargetVisited(JNIEnv* j_env,
const base::android::JavaRef<jobject>& j_this, const base::android::JavaRef<jobject>& j_this,
const jlong visit_time_ms, const jlong visit_time_ms,
const jboolean is_offline); const jboolean is_offline,
const jboolean return_to_ntp);
private: private:
FeedLoggingMetrics* feed_logging_metrics_; FeedLoggingMetrics* feed_logging_metrics_;
......
...@@ -53,6 +53,9 @@ void RecordContentSuggestionsUsage() { ...@@ -53,6 +53,9 @@ void RecordContentSuggestionsUsage() {
base::UmaHistogramExactLinear(histogram_name, bucket, kNumBuckets); base::UmaHistogramExactLinear(histogram_name, bucket, kNumBuckets);
UMA_HISTOGRAM_EXACT_LINEAR(kHistogramArticlesUsageTimeLocal, bucket, UMA_HISTOGRAM_EXACT_LINEAR(kHistogramArticlesUsageTimeLocal, bucket,
kNumBuckets); kNumBuckets);
base::RecordAction(
base::UserMetricsAction("NewTabPage_ContentSuggestions_ArticlesUsage"));
} }
int ToUMAScore(float score) { int ToUMAScore(float score) {
...@@ -64,6 +67,14 @@ int ToUMAScore(float score) { ...@@ -64,6 +67,14 @@ int ToUMAScore(float score) {
return ceil(score * 10); return ceil(score * 10);
} }
void RecordSuggestionPageVisited(bool return_to_ntp) {
if (return_to_ntp) {
base::RecordAction(
base::UserMetricsAction("MobileNTP.Snippets.VisitEndBackInNTP"));
}
base::RecordAction(base::UserMetricsAction("MobileNTP.Snippets.VisitEnd"));
}
} // namespace } // namespace
FeedLoggingMetrics::FeedLoggingMetrics( FeedLoggingMetrics::FeedLoggingMetrics(
...@@ -125,6 +136,8 @@ void FeedLoggingMetrics::OnSuggestionOpened(int position, ...@@ -125,6 +136,8 @@ void FeedLoggingMetrics::OnSuggestionOpened(int position,
ToUMAScore(score), 11); ToUMAScore(score), 11);
RecordContentSuggestionsUsage(); RecordContentSuggestionsUsage();
base::RecordAction(base::UserMetricsAction("Suggestions.Content.Opened"));
} }
void FeedLoggingMetrics::OnSuggestionWindowOpened( void FeedLoggingMetrics::OnSuggestionWindowOpened(
...@@ -135,6 +148,10 @@ void FeedLoggingMetrics::OnSuggestionWindowOpened( ...@@ -135,6 +148,10 @@ void FeedLoggingMetrics::OnSuggestionWindowOpened(
"NewTabPage.ContentSuggestions.OpenDisposition.Articles", "NewTabPage.ContentSuggestions.OpenDisposition.Articles",
static_cast<int>(disposition), static_cast<int>(disposition),
static_cast<int>(WindowOpenDisposition::MAX_VALUE) + 1); static_cast<int>(WindowOpenDisposition::MAX_VALUE) + 1);
if (disposition == WindowOpenDisposition::CURRENT_TAB) {
base::RecordAction(base::UserMetricsAction("Suggestions.Card.Tapped"));
}
} }
void FeedLoggingMetrics::OnSuggestionMenuOpened(int position, void FeedLoggingMetrics::OnSuggestionMenuOpened(int position,
...@@ -157,18 +174,23 @@ void FeedLoggingMetrics::OnSuggestionDismissed(int position, const GURL& url) { ...@@ -157,18 +174,23 @@ void FeedLoggingMetrics::OnSuggestionDismissed(int position, const GURL& url) {
history_url_check_callback_.Run( history_url_check_callback_.Run(
url, base::BindOnce(&FeedLoggingMetrics::CheckURLVisitedDone, url, base::BindOnce(&FeedLoggingMetrics::CheckURLVisitedDone,
weak_ptr_factory_.GetWeakPtr(), position)); weak_ptr_factory_.GetWeakPtr(), position));
base::RecordAction(base::UserMetricsAction("Suggestions.Content.Dismissed"));
} }
void FeedLoggingMetrics::OnSuggestionArticleVisited( void FeedLoggingMetrics::OnSuggestionArticleVisited(base::TimeDelta visit_time,
base::TimeDelta visit_time) { bool return_to_ntp) {
base::UmaHistogramLongTimes( base::UmaHistogramLongTimes(
"NewTabPage.ContentSuggestions.VisitDuration.Articles", visit_time); "NewTabPage.ContentSuggestions.VisitDuration.Articles", visit_time);
RecordSuggestionPageVisited(return_to_ntp);
} }
void FeedLoggingMetrics::OnSuggestionOfflinePageVisited( void FeedLoggingMetrics::OnSuggestionOfflinePageVisited(
base::TimeDelta visit_time) { base::TimeDelta visit_time,
bool return_to_ntp) {
base::UmaHistogramLongTimes( base::UmaHistogramLongTimes(
"NewTabPage.ContentSuggestions.VisitDuration.Downloads", visit_time); "NewTabPage.ContentSuggestions.VisitDuration.Downloads", visit_time);
RecordSuggestionPageVisited(return_to_ntp);
} }
void FeedLoggingMetrics::OnMoreButtonShown(int position) { void FeedLoggingMetrics::OnMoreButtonShown(int position) {
......
...@@ -56,9 +56,11 @@ class FeedLoggingMetrics { ...@@ -56,9 +56,11 @@ class FeedLoggingMetrics {
void OnSuggestionDismissed(int position, const GURL& url); void OnSuggestionDismissed(int position, const GURL& url);
void OnSuggestionArticleVisited(base::TimeDelta visit_time); void OnSuggestionArticleVisited(base::TimeDelta visit_time,
bool return_to_ntp);
void OnSuggestionOfflinePageVisited(base::TimeDelta visit_time); void OnSuggestionOfflinePageVisited(base::TimeDelta visit_time,
bool return_to_ntp);
// Should only be called once per NTP for each "more" button. // Should only be called once per NTP for each "more" button.
void OnMoreButtonShown(int position); void OnMoreButtonShown(int position);
......
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