blob: cee92bf017291e181257ad58392272d9c008b359 [file] [log] [blame]
// Copyright 2021 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 CHROME_BROWSER_UI_USER_EDUCATION_SCOPED_NEW_BADGE_TRACKER_H_
#define CHROME_BROWSER_UI_USER_EDUCATION_SCOPED_NEW_BADGE_TRACKER_H_
#include <set>
namespace base {
struct Feature;
}
namespace content {
class BrowserContext;
}
namespace feature_engagement {
class Tracker;
}
// Works with the Feature Engagement system to determine when/how many times a
// New Badge is displayed to the user. Wraps a feature_engagement::Tracker so
// the correct calls are made to the Feature Engagement backend.
//
// The lifespan of a ScopedNewBadgeTracker should match the time the dialog or
// menu containing the New Badge is visible to the user.
//
// You may use a single ScopedNewBadgeTracker for New Badges on multiple
// features in the same menu or dialog, but make sure the feature flags and
// event names are distinct.
//
// Usage:
//
// * Menus
//
// Below is an example of using a ScopedNewBadgeTracker to add a New Badge to a
// menu where the object implementing ui::SimpleMenuModel::Delegate is created
// each time the menu is shown (e.g. AppMenuModel, TabContextMenuContents,
// etc.) The case where the delegate object is persistent will be discussed
// later.
//
// // Menu model constructor:
// MyMenuModelDelegate::MyMenuModelDelegate(Browser* browser, ...) : ...
// new_badge_tracker_(browser->profile()),
//
// // In OnMenuWillShow(menu):
// menu->SetIsNewFeatureAt(
// menu->GetIndexOfCommandId(IDC_MY_FEATURE),
// new_badge_tracker_.TryShowNewBadge(
// feature_engagement::kIPHMyFeatureNewBadge,
// &ui_features::kMyFeature));
//
// // In ExecuteCommand():
// case IDC_MY_FEATURE:
// new_badge_tracker_.EventPerformed("my_feature_activated");
// ...
// break;
//
// If the New Badge is in the top-level menu, you can move the call to
// SetIsNewFeatureAt() to immediately after the menu model is initialized
// (typically in the constructor or "Init" method) and you will not have to
// override OnMenuWillBeShown().
//
// If you are handling multiple New Badges for different features, you will
// want to check the result of GetIndexOfCommand() to make sure the menu being
// shown is the one that contains the item receiving the new badge.
//
// If the ui::SimpleMenuModel::Delegate is a persistent object and is not
// created each time the menu is displayed, you will need to move the tracker
// down into the ui::SimpleMenuModel for your menu, and move your code from
// OnMenuWillShow() to MenuWillShow() and from ExecuteCommand() to
// ActivatedAt(int, int). Be sure to invoke base class behavior when overriding
// these methods!
//
// * Dialogs
//
// Add a ScopedNewBadgeTracker member variable to your DialogDelegateView.
// Dialogs are typically not re-usable; we create a new DialogDelegateView for
// each time we show them. If you are following this pattern, include this in
// your constructor or "Init" function after creating the NewBadgeLabel (but
// before showing the dialog):
//
// new_badge_label_->SetDisplayNewBadge(
// new_badge_tracker_.TryShowNewBadge(
// feature_engagement::kIPHMyFeatureNewBadge,
// &ui_features::kMyFeature));
//
// Then in the callback for the button that activates the feature being
// promoted, call:
//
// new_badge_tracker_.EventPerformed("my_feature_activated");
//
// If for some reason you are re-using a dialog delegate, you must dynamically
// create and destroy the tracker when the dialog is shown and hidden.
class ScopedNewBadgeTracker {
public:
// Constructs a scoped tracker for browser with |profile|.
explicit ScopedNewBadgeTracker(content::BrowserContext* profile);
// This object should be destructed when the New Badge is going away, such as
// when a menu with a New Badge or a dialog with a NewBadgeLabel is closing.
// If TryShowNewBadge() returned true, the tracker will be informed that the
// promo has ended.
~ScopedNewBadgeTracker();
ScopedNewBadgeTracker(const ScopedNewBadgeTracker& other) = delete;
void operator=(const ScopedNewBadgeTracker& other) = delete;
// Returns whether the New Badge should be shown.
//
// |badge_feature| is the feature flag for the New Badge itself.
//
// |promoted_feature|, if specified, is the flag for the feature the New Badge
// is promoting. You generally want to specify this feature even if the two
// flags are controlled by the same Finch study, because the user could
// override one but not the other. This parameter is optional because a New
// Badge promo could be shown for a feature without a flag, or for a feature
// that has already rolled to 100% and whose flag has been removed.
bool TryShowNewBadge(const base::Feature& badge_feature,
const base::Feature* promoted_feature = nullptr);
// Indicates that the user performed a promoted action. |action_event_name|
// should be the value referenced in the "event_used" parameter of your field
// trial configuration.
// Note: this is a wrapper around feature_engagement::Tracker::NotifyEvent().
void ActionPerformed(const char* action_event_name);
private:
feature_engagement::Tracker* const tracker_;
std::set<const base::Feature*> active_badge_features_;
};
#endif // CHROME_BROWSER_UI_USER_EDUCATION_SCOPED_NEW_BADGE_TRACKER_H_