chromium/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageUma.java

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.ntp;

import androidx.annotation.IntDef;

import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.RecordUserAction;
import org.chromium.chrome.browser.feed.FeedFeatures;
import org.chromium.chrome.browser.preferences.Pref;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabCreationState;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
import org.chromium.chrome.browser.util.BrowserUiUtils;
import org.chromium.chrome.browser.util.BrowserUiUtils.ModuleTypeOnStartAndNtp;
import org.chromium.components.embedder_support.util.UrlUtilities;
import org.chromium.components.embedder_support.util.UrlUtilitiesJni;
import org.chromium.components.user_prefs.UserPrefs;
import org.chromium.ui.base.PageTransition;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Records UMA stats for which actions the user takes on the NTP in the
 * "NewTabPage.ActionAndroid2" histogram.
 */
public class NewTabPageUma {
    // Possible actions taken by the user on the NTP. These values are also defined in
    // enums.xml as NewTabPageActionAndroid2.
    // WARNING: these values must stay in sync with enums.xml.

    /** User performed a search using the omnibox. */
    private static final int ACTION_SEARCHED_USING_OMNIBOX = 0;

    /** User navigated to Google search homepage using the omnibox. */
    private static final int ACTION_NAVIGATED_TO_GOOGLE_HOMEPAGE = 1;

    /** User navigated to any other page using the omnibox. */
    private static final int ACTION_NAVIGATED_USING_OMNIBOX = 2;

    /** User opened a most visited tile. */
    public static final int ACTION_OPENED_MOST_VISITED_TILE = 3;

    /** User opened the recent tabs manager. */
    public static final int ACTION_OPENED_RECENT_TABS_MANAGER = 4;

    /** User opened the history manager. */
    public static final int ACTION_OPENED_HISTORY_MANAGER = 5;

    /** User opened the bookmarks manager. */
    public static final int ACTION_OPENED_BOOKMARKS_MANAGER = 6;

    /** User opened the downloads manager. */
    public static final int ACTION_OPENED_DOWNLOADS_MANAGER = 7;

    /** User navigated to the webpage for a snippet shown on the NTP. */
    public static final int ACTION_OPENED_SNIPPET = 8;

    /** User clicked on the "learn more" link in the footer or in the feed header menu. */
    public static final int ACTION_CLICKED_LEARN_MORE = 9;

    /** (Obsolete) User clicked on the "Refresh" button in the "all dismissed" state. */
    // public static final int ACTION_CLICKED_ALL_DISMISSED_REFRESH = 10;

    /** (Obsolete) User opened an explore sites tile. */
    // public static final int ACTION_OPENED_EXPLORE_SITES_TILE = 11;

    /**
     * (Obsolete) User clicked on the "Manage Interests" item in the snippet card menu or in the
     * feed header menu.
     */
    // public static final int ACTION_CLICKED_MANAGE_INTERESTS = 12;

    /** (Obsolete) User triggered a block content action. * */
    // public static final int ACTION_BLOCK_CONTENT = 13;

    /** (Obsolete) User clicked on the "Manage activity" item in the feed header menu. */
    // public static final int ACTION_CLICKED_MANAGE_ACTIVITY = 14;

    /** (Obsolete) User clicked on the feed header menu button item in the feed header menu. */
    // public static final int ACTION_CLICKED_FEED_HEADER_MENU = 15;

    /** (Obsolete) User clicked to play the full video for a video snippet shown on the NTP. */
    // public static final int ACTION_OPENED_VIDEO = 16;

    /** The number of possible actions. */
    private static final int NUM_ACTIONS = 17;

    /** Regular NTP impression (usually when a new tab is opened). */
    public static final int NTP_IMPRESSION_REGULAR = 0;

    /** Potential NTP impressions (instead of blank page if no tab is open). */
    public static final int NTP_IMPRESSION_POTENTIAL_NO_TAB = 1;

    /** The number of possible NTP impression types */
    private static final int NUM_NTP_IMPRESSION = 2;

    /**
     * Possible results when updating content suggestions list in the UI. Keep in sync with the
     * ContentSuggestionsUIUpdateResult2 enum in enums.xml. Do not remove or change existing
     * values other than NUM_UI_UPDATE_RESULTS.
     */
    @IntDef({
        ContentSuggestionsUIUpdateResult.SUCCESS_APPENDED,
        ContentSuggestionsUIUpdateResult.SUCCESS_REPLACED,
        ContentSuggestionsUIUpdateResult.FAIL_ALL_SEEN,
        ContentSuggestionsUIUpdateResult.FAIL_DISABLED
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface ContentSuggestionsUIUpdateResult {
        /**
         * The content suggestions are successfully appended (because they are set for the first
         * time or explicitly marked to be appended).
         */
        int SUCCESS_APPENDED = 0;

        /**
         * Update successful, suggestions were replaced (some of them possibly seen, the exact
         * number reported in a separate histogram).
         */
        int SUCCESS_REPLACED = 1;

        /** Update failed, all previous content suggestions have been seen (and kept). */
        int FAIL_ALL_SEEN = 2;

        /** Update failed, because it is disabled by a variation parameter. */
        int FAIL_DISABLED = 3;

        int NUM_ENTRIES = 4;
    }

    // These values are persisted to logs. Entries should not be renumbered and
    // numeric values should never be reused. This maps directly to
    // the ContentSuggestionsDisplayStatus enum defined in tools/metrics/histograms/enums.xml.
    @IntDef({
        ContentSuggestionsDisplayStatus.VISIBLE,
        ContentSuggestionsDisplayStatus.COLLAPSED,
        ContentSuggestionsDisplayStatus.DISABLED_BY_POLICY,
        ContentSuggestionsDisplayStatus.DISABLED,
        ContentSuggestionsDisplayStatus.DISABLED_BY_DSE
    })
    @Retention(RetentionPolicy.SOURCE)
    private @interface ContentSuggestionsDisplayStatus {
        int VISIBLE = 0;
        int COLLAPSED = 1;
        int DISABLED_BY_POLICY = 2;
        int DISABLED = 3;
        int DISABLED_BY_DSE = 4;
        int NUM_ENTRIES = 5;
    }

    private final TabModelSelector mTabModelSelector;
    private TabCreationRecorder mTabCreationRecorder;

    /**
     * Constructor.
     * @param tabModelSelector Tab model selector to observe tab creation event.
     */
    public NewTabPageUma(TabModelSelector tabModelSelector) {
        mTabModelSelector = tabModelSelector;
    }

    /**
     * Records an action taken by the user on the NTP.
     * @param action One of the ACTION_* values defined in this class.
     */
    public static void recordAction(int action) {
        assert action >= 0;
        assert action < NUM_ACTIONS;
        RecordHistogram.recordEnumeratedHistogram("NewTabPage.ActionAndroid2", action, NUM_ACTIONS);
    }

    /**
     * Record that the user has navigated away from the NTP using the omnibox.
     * @param destinationUrl The URL to which the user navigated.
     * @param transitionType The transition type of the navigation, from PageTransition.java.
     * @param isNtp Whether the current page is a {@link NewTabPage}.
     */
    public static void recordOmniboxNavigation(
            String destinationUrl, int transitionType, boolean isNtp) {
        if ((transitionType & PageTransition.CORE_MASK) == PageTransition.GENERATED) {
            recordAction(ACTION_SEARCHED_USING_OMNIBOX);
        } else {
            if (UrlUtilitiesJni.get().isGoogleHomePageUrl(destinationUrl)) {
                recordAction(ACTION_NAVIGATED_TO_GOOGLE_HOMEPAGE);
            } else {
                recordAction(ACTION_NAVIGATED_USING_OMNIBOX);
            }
        }
        if (isNtp) {
            BrowserUiUtils.recordModuleClickHistogram(ModuleTypeOnStartAndNtp.OMNIBOX);
        }
    }

    /**
     * Record a NTP impression (even potential ones to make informed product decisions). If the
     * impression type is {@link NewTabPageUma#NTP_IMPRESSION_REGULAR}, also records a user action.
     *
     * @param impressionType Type of the impression from NewTabPageUma.java
     */
    public static void recordNtpImpression(int impressionType) {
        assert impressionType >= 0;
        assert impressionType < NUM_NTP_IMPRESSION;
        RecordHistogram.recordEnumeratedHistogram(
                "Android.NTP.Impression", impressionType, NUM_NTP_IMPRESSION);
    }

    /**
     * Records how often new tabs with a NewTabPage are created. This helps to determine how often
     * users navigate back to already opened NTPs.
     */
    public void monitorNtpCreation() {
        mTabCreationRecorder = new TabCreationRecorder();
        mTabModelSelector.addObserver(mTabCreationRecorder);
    }

    /** Records Content Suggestions Display Status when NTPs opened. */
    public void recordContentSuggestionsDisplayStatus(Profile profile) {
        @ContentSuggestionsDisplayStatus int status = ContentSuggestionsDisplayStatus.VISIBLE;
        if (!UserPrefs.get(profile).getBoolean(Pref.ENABLE_SNIPPETS)) {
            // Disabled by policy.
            status = ContentSuggestionsDisplayStatus.DISABLED_BY_POLICY;
        } else if (!UserPrefs.get(profile).getBoolean(Pref.ENABLE_SNIPPETS_BY_DSE)) {
            // Disabled when swapping NTP is enabled and the default search engine isn't Google.
            status = ContentSuggestionsDisplayStatus.DISABLED_BY_DSE;
        } else if (!FeedFeatures.isFeedEnabled(profile)) {
            status = ContentSuggestionsDisplayStatus.DISABLED;
        } else if (!UserPrefs.get(profile).getBoolean(Pref.ARTICLES_LIST_VISIBLE)) {
            // Articles are collapsed.
            status = ContentSuggestionsDisplayStatus.COLLAPSED;
        }

        RecordHistogram.recordEnumeratedHistogram(
                "ContentSuggestions.Feed.DisplayStatusOnOpen",
                status,
                ContentSuggestionsDisplayStatus.NUM_ENTRIES);
    }

    /**
     * Records the number of new NTPs opened in a new tab. Use through {@link
     * NewTabPageUma#monitorNtpCreation(TabModelSelector)}.
     */
    private static class TabCreationRecorder implements TabModelSelectorObserver {
        @Override
        public void onNewTabCreated(Tab tab, @TabCreationState int creationState) {
            if (!UrlUtilities.isNtpUrl(tab.getUrl())) return;
            RecordUserAction.record("MobileNTPOpenedInNewTab");
        }
    }

    /** Destroy and unhook objects at destruction. */
    public void destroy() {
        if (mTabCreationRecorder != null) mTabModelSelector.removeObserver(mTabCreationRecorder);
    }
}