1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
|
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_ASH_HATS_HATS_NOTIFICATION_CONTROLLER_H_
#define CHROME_BROWSER_ASH_HATS_HATS_NOTIFICATION_CONTROLLER_H_
#include "base/containers/flat_map.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "chrome/browser/profiles/profile_observer.h"
#include "chromeos/ash/components/network/network_state_handler_observer.h"
#include "ui/message_center/public/cpp/notification_delegate.h"
namespace message_center {
class Notification;
} // namespace message_center
class Profile;
class NetworkState;
namespace ash {
struct HatsConfig;
// TODO(jackshira): Extract non-notification specific code into a manager class.
// Happiness tracking survey (HaTS) notification controller is responsible for
// managing the HaTS notification that is displayed to the user.
// This class lives on the UI thread.
class HatsNotificationController : public message_center::NotificationDelegate,
public NetworkStateHandlerObserver,
public ProfileObserver {
public:
static const char kNotificationId[];
// Minimum amount of time before the notification is displayed again after a
// user has interacted with it.
static constexpr base::TimeDelta kHatsThreshold = base::Days(60);
// The threshold for a Googler is less.
static constexpr base::TimeDelta kHatsGooglerThreshold = base::Days(30);
// Prioritized HaTS has much shorter threshold.
static constexpr base::TimeDelta kPrioritizedHatsThreshold = base::Days(10);
// There are multiple pool/quota of cooldowns: normal and prioritized,
// when user was selected for one pool, there need to be another cooldown
// to ensure the user would not be selected for the other pool immediately
// after.
static constexpr base::TimeDelta kMinimumHatsThreshold = base::Days(1);
// HaTS threshold should be configured correctly.
static_assert(kHatsThreshold > kPrioritizedHatsThreshold);
static_assert(kHatsThreshold > kHatsGooglerThreshold);
static_assert(kPrioritizedHatsThreshold > kMinimumHatsThreshold);
static_assert(kHatsGooglerThreshold > kMinimumHatsThreshold);
HatsNotificationController(
Profile* profile,
const HatsConfig& config,
const base::flat_map<std::string, std::string>& product_specific_data,
std::u16string title,
std::u16string body);
// |product_specific_data| is meant to allow attaching extra runtime data that
// is specific to the survey, e.g. a survey about the log-in experience might
// include the last used authentication method.
HatsNotificationController(
Profile* profile,
const HatsConfig& config,
const base::flat_map<std::string, std::string>& product_specific_data);
HatsNotificationController(Profile* profile, const HatsConfig& config);
HatsNotificationController(const HatsNotificationController&) = delete;
HatsNotificationController& operator=(const HatsNotificationController&) =
delete;
// Returns true if the survey needs to be displayed for the given |profile|.
static bool ShouldShowSurveyToProfile(Profile* profile,
const HatsConfig& config);
private:
friend class HatsNotificationControllerTest;
FRIEND_TEST_ALL_PREFIXES(HatsNotificationControllerTest,
GetFormattedSiteContext);
FRIEND_TEST_ALL_PREFIXES(HatsNotificationControllerTest,
NewDevice_ShouldNotShowNotification);
FRIEND_TEST_ALL_PREFIXES(HatsNotificationControllerTest,
OldDevice_ShouldShowNotification);
FRIEND_TEST_ALL_PREFIXES(HatsNotificationControllerTest,
NoInternet_DoNotShowNotification);
FRIEND_TEST_ALL_PREFIXES(HatsNotificationControllerTest,
InternetConnected_ShowNotification);
FRIEND_TEST_ALL_PREFIXES(HatsNotificationControllerTest,
DismissNotification_ShouldUpdatePref);
FRIEND_TEST_ALL_PREFIXES(
HatsNotificationControllerTest,
Disconnected_RemoveNotification_Connected_AddNotification);
FRIEND_TEST_ALL_PREFIXES(HatsNotificationControllerTest,
DismissNotification_PrioritizedShouldUpdatePref);
~HatsNotificationController() override;
enum class HatsState {
kDeviceSelected = 0, // Device was selected in roll of dice.
kSurveyShownRecently = 1, // A survey was shown recently on device.
kNewDevice = 2, // Device is too new to show the survey.
kNotificationDisplayed = 3, // Pop up for survey was presented to user.
kNotificationDismissed = 4, // Notification was dismissed by user.
kNotificationClicked = 5, // User clicked on notification to open the
// survey.
kMaxValue = kNotificationClicked
};
void Initialize(bool is_new_device);
// NotificationDelegate overrides:
void Close(bool by_user) override;
void Click(const std::optional<int>& button_index,
const std::optional<std::u16string>& reply) override;
// NetworkStateHandlerObserver override:
void PortalStateChanged(const NetworkState* default_network,
NetworkState::PortalState portal_state) override;
void OnShuttingDown() override;
// ProfileObserver:
void OnProfileWillBeDestroyed(Profile* profile) override;
// Must be run on a blocking thread pool.
// Gathers the browser version info, firmware info and platform info and
// returns them in a single encoded string, in the format
// "<key>=<value>&<key>=<value>&<key>=<value>" where the keys and values are
// url-escaped. Any key-value pairs in |product_specific_data| are also
// encoded and appended to the string, unless the keys collide with existing
// device info keys.
static std::string GetFormattedSiteContext(
const std::string& user_locale,
const base::flat_map<std::string, std::string>& product_specific_data);
void UpdateLastInteractionTime();
void UpdateLastSurveyInteractionTime();
void ShowDialog(const std::string& site_context);
raw_ptr<Profile> profile_;
const raw_ref<const HatsConfig> hats_config_;
base::flat_map<std::string, std::string> product_specific_data_;
std::unique_ptr<message_center::Notification> notification_;
const std::u16string title_;
const std::u16string body_;
HatsState state_ = HatsState::kDeviceSelected;
base::ScopedObservation<Profile, ProfileObserver> profile_observation_{this};
base::WeakPtrFactory<HatsNotificationController> weak_pointer_factory_{this};
};
} // namespace ash
#endif // CHROME_BROWSER_ASH_HATS_HATS_NOTIFICATION_CONTROLLER_H_
|