1 // Copyright 2018 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ash/multi_device_setup/multi_device_notification_presenter.h"
10 #include "ash/constants/ash_features.h"
11 #include "ash/constants/notifier_catalogs.h"
12 #include "ash/public/cpp/notification_utils.h"
13 #include "ash/public/cpp/system_tray_client.h"
14 #include "ash/resources/vector_icons/vector_icons.h"
15 #include "ash/session/session_controller_impl.h"
16 #include "ash/shell.h"
17 #include "ash/shell_delegate.h"
18 #include "ash/strings/grit/ash_strings.h"
19 #include "ash/system/model/system_tray_model.h"
20 #include "base/functional/callback_helpers.h"
21 #include "base/memory/ptr_util.h"
22 #include "base/metrics/histogram_functions.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "chromeos/ash/components/multidevice/logging/logging.h"
25 #include "ui/base/l10n/l10n_util.h"
26 #include "ui/chromeos/devicetype_utils.h"
27 #include "ui/message_center/message_center.h"
28 #include "ui/message_center/public/cpp/notifier_id.h"
34 bool g_disable_notifications_for_test_ = false;
36 const char kNotifierMultiDevice[] = "ash.multi_device_setup";
41 const char MultiDeviceNotificationPresenter::kSetupNotificationId[] =
42 "cros_multi_device_setup_notification_id";
45 const char MultiDeviceNotificationPresenter::kWifiSyncNotificationId[] =
46 "cros_wifi_sync_announcement_notification_id";
50 MultiDeviceNotificationPresenter::GetNotificationDescriptionForLogging(
51 Status notification_status) {
52 switch (notification_status) {
53 case Status::kNewUserNotificationVisible:
54 return "notification to prompt setup";
55 case Status::kExistingUserHostSwitchedNotificationVisible:
56 return "notification of switch to new host";
57 case Status::kExistingUserNewChromebookNotificationVisible:
58 return "notification of new Chromebook added";
59 case Status::kNoNotificationVisible:
60 return "no notification";
66 MultiDeviceNotificationPresenter::NotificationType
67 MultiDeviceNotificationPresenter::GetMetricValueForNotification(
68 Status notification_status) {
69 switch (notification_status) {
70 case Status::kNewUserNotificationVisible:
71 return NotificationType::kNewUserPotentialHostExists;
72 case Status::kExistingUserHostSwitchedNotificationVisible:
73 return NotificationType::kExistingUserHostSwitched;
74 case Status::kExistingUserNewChromebookNotificationVisible:
75 return NotificationType::kExistingUserNewChromebookAdded;
76 case Status::kNoNotificationVisible:
78 return NotificationType::kErrorUnknown;
82 MultiDeviceNotificationPresenter::MultiDeviceNotificationPresenter(
83 message_center::MessageCenter* message_center)
84 : message_center_(message_center) {
85 DCHECK(message_center_);
87 Shell::Get()->session_controller()->AddObserver(this);
89 // If the constructor is called after the session state has already been set
90 // (e.g., if recovering from a crash), handle that now. If the user has not
91 // yet logged in, this will be a no-op.
92 ObserveMultiDeviceSetupIfPossible();
95 MultiDeviceNotificationPresenter::~MultiDeviceNotificationPresenter() {
96 message_center_->RemoveObserver(this);
97 Shell::Get()->session_controller()->RemoveObserver(this);
100 void MultiDeviceNotificationPresenter::OnPotentialHostExistsForNewUser() {
101 int title_message_id =
102 IDS_ASH_MULTI_DEVICE_SETUP_NEW_USER_POTENTIAL_HOST_EXISTS_TITLE;
103 if (features::IsPhoneHubOnboardingNotifierRevampEnabled() &&
104 !features::kPhoneHubOnboardingNotifierUseNudge.Get()) {
106 features::kPhoneHubNotifierTextGroup.Get() ==
107 features::PhoneHubNotifierTextGroup::kNotifierTextGroupA
108 ? IDS_ASH_MULTI_DEVICE_SETUP_NOTIFIER_TEXT_WITH_PHONE_HUB
109 : IDS_ASH_MULTI_DEVICE_SETUP_NOTIFIER_TEXT_WITHOUT_PHONE_HUB;
111 std::u16string title = l10n_util::GetStringUTF16(title_message_id);
112 std::u16string message = l10n_util::GetStringFUTF16(
113 IDS_ASH_MULTI_DEVICE_SETUP_NEW_USER_POTENTIAL_HOST_EXISTS_MESSAGE,
114 ui::GetChromeOSDeviceName());
115 ShowSetupNotification(Status::kNewUserNotificationVisible, title, message);
118 void MultiDeviceNotificationPresenter::OnNoLongerNewUser() {
119 if (notification_status_ != Status::kNewUserNotificationVisible)
121 RemoveMultiDeviceSetupNotification();
124 void MultiDeviceNotificationPresenter::OnConnectedHostSwitchedForExistingUser(
125 const std::string& new_host_device_name) {
126 std::u16string title = l10n_util::GetStringFUTF16(
127 IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_HOST_SWITCHED_TITLE,
128 base::ASCIIToUTF16(new_host_device_name));
129 std::u16string message = l10n_util::GetStringFUTF16(
130 IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_HOST_SWITCHED_MESSAGE,
131 ui::GetChromeOSDeviceName());
132 ShowSetupNotification(Status::kExistingUserHostSwitchedNotificationVisible,
136 void MultiDeviceNotificationPresenter::OnNewChromebookAddedForExistingUser(
137 const std::string& new_host_device_name) {
138 std::u16string title = l10n_util::GetStringFUTF16(
139 IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_NEW_CHROME_DEVICE_ADDED_TITLE,
140 base::ASCIIToUTF16(new_host_device_name));
141 std::u16string message = l10n_util::GetStringFUTF16(
142 IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_NEW_CHROME_DEVICE_ADDED_MESSAGE,
143 ui::GetChromeOSDeviceName());
144 ShowSetupNotification(Status::kExistingUserNewChromebookNotificationVisible,
148 void MultiDeviceNotificationPresenter::OnBecameEligibleForWifiSync() {
149 std::u16string title =
150 l10n_util::GetStringUTF16(IDS_ASH_MULTI_DEVICE_WIFI_SYNC_AVAILABLE_TITLE);
151 std::u16string message = l10n_util::GetStringFUTF16(
152 IDS_ASH_MULTI_DEVICE_WIFI_SYNC_AVAILABLE_MESSAGE,
153 ui::GetChromeOSDeviceName());
154 message_center::RichNotificationData optional_fields;
155 optional_fields.buttons.push_back(
156 message_center::ButtonInfo(l10n_util::GetStringUTF16(
157 IDS_ASH_MULTI_DEVICE_WIFI_SYNC_AVAILABLE_TURN_ON_BUTTON)));
158 optional_fields.buttons.push_back(
159 message_center::ButtonInfo(l10n_util::GetStringUTF16(
160 IDS_ASH_MULTI_DEVICE_WIFI_SYNC_AVAILABLE_CANCEL_BUTTON)));
162 ShowNotification(kWifiSyncNotificationId, title, message, optional_fields);
163 base::UmaHistogramEnumeration("MultiDeviceSetup_NotificationShown",
164 NotificationType::kWifiSyncAnnouncement);
168 std::unique_ptr<base::AutoReset<bool>>
169 MultiDeviceNotificationPresenter::DisableNotificationsForTesting() {
170 return std::make_unique<base::AutoReset<bool>>(
171 &g_disable_notifications_for_test_, true);
174 void MultiDeviceNotificationPresenter::RemoveMultiDeviceSetupNotification() {
175 notification_status_ = Status::kNoNotificationVisible;
176 message_center_->RemoveNotification(kSetupNotificationId,
177 /* by_user */ false);
180 void MultiDeviceNotificationPresenter::UpdateIsSetupNotificationInteracted(
181 bool is_setup_notification_interacted) {
182 is_setup_notification_interacted_ = is_setup_notification_interacted;
185 void MultiDeviceNotificationPresenter::OnUserSessionAdded(
186 const AccountId& account_id) {
187 ObserveMultiDeviceSetupIfPossible();
190 void MultiDeviceNotificationPresenter::OnSessionStateChanged(
191 session_manager::SessionState state) {
192 ObserveMultiDeviceSetupIfPossible();
195 void MultiDeviceNotificationPresenter::OnNotificationRemoved(
196 const std::string& notification_id,
201 if (notification_id == kSetupNotificationId) {
202 base::UmaHistogramEnumeration(
203 "MultiDeviceSetup_NotificationDismissed",
204 GetMetricValueForNotification(notification_status_));
207 if (notification_id == kWifiSyncNotificationId) {
208 base::UmaHistogramEnumeration("MultiDeviceSetup_NotificationDismissed",
209 NotificationType::kWifiSyncAnnouncement);
214 void MultiDeviceNotificationPresenter::OnNotificationClicked(
215 const std::string& notification_id,
216 const absl::optional<int>& button_index,
217 const absl::optional<std::u16string>& reply) {
218 if (notification_id == kWifiSyncNotificationId) {
219 message_center_->RemoveNotification(kWifiSyncNotificationId,
220 /* by_user */ false);
223 switch (*button_index) {
224 case 0: // "Turn on" button
225 PA_LOG(INFO) << "Enabling Wi-Fi Sync.";
226 multidevice_setup_remote_->SetFeatureEnabledState(
227 multidevice_setup::mojom::Feature::kWifiSync,
228 /*enabled=*/true, /*auth_token=*/absl::nullopt,
229 /*callback=*/base::DoNothing());
231 case 1: // "Cancel" button
232 base::UmaHistogramEnumeration(
233 "MultiDeviceSetup_NotificationDismissed",
234 NotificationType::kWifiSyncAnnouncement);
239 Shell::Get()->system_tray_model()->client()->ShowWifiSyncSettings();
240 base::UmaHistogramEnumeration("MultiDeviceSetup_NotificationClicked",
241 NotificationType::kWifiSyncAnnouncement);
245 if (notification_id != kSetupNotificationId)
248 DCHECK(notification_status_ != Status::kNoNotificationVisible);
249 PA_LOG(VERBOSE) << "User clicked "
250 << GetNotificationDescriptionForLogging(notification_status_)
252 base::UmaHistogramEnumeration(
253 "MultiDeviceSetup_NotificationClicked",
254 GetMetricValueForNotification(notification_status_));
255 switch (notification_status_) {
256 case Status::kNewUserNotificationVisible:
257 Shell::Get()->system_tray_model()->client()->ShowMultiDeviceSetup();
258 // If user has not interacted with Phone Hub icon when the notification is
259 // visible, log MultiDeviceSetup.NotificationInteracted event when
260 // notification is clicked.
261 if (!is_setup_notification_interacted_) {
262 base::UmaHistogramCounts100("MultiDeviceSetup.NotificationInteracted",
265 // Restore the value when the notification is clicked.
266 UpdateIsSetupNotificationInteracted(false);
269 case Status::kExistingUserHostSwitchedNotificationVisible:
270 // Clicks on the 'host switched' and 'Chromebook added' notifications have
271 // the same effect, i.e. opening the Settings subpage.
273 case Status::kExistingUserNewChromebookNotificationVisible:
275 ->system_tray_model()
277 ->ShowConnectedDevicesSettings();
279 case Status::kNoNotificationVisible:
282 RemoveMultiDeviceSetupNotification();
285 void MultiDeviceNotificationPresenter::ObserveMultiDeviceSetupIfPossible() {
286 // If already the delegate, there is nothing else to do.
287 if (multidevice_setup_remote_)
290 const SessionControllerImpl* session_controller =
291 Shell::Get()->session_controller();
293 if (session_controller->GetSessionState() !=
294 session_manager::SessionState::ACTIVE) {
298 const UserSession* user_session = session_controller->GetPrimaryUserSession();
300 // The primary user session may be unavailable (e.g., for test/guest users).
304 Shell::Get()->shell_delegate()->BindMultiDeviceSetup(
305 multidevice_setup_remote_.BindNewPipeAndPassReceiver());
307 // Add this object as the delegate of the MultiDeviceSetup Service.
308 multidevice_setup_remote_->SetAccountStatusChangeDelegate(
309 receiver_.BindNewPipeAndPassRemote());
311 message_center_->AddObserver(this);
314 void MultiDeviceNotificationPresenter::ShowSetupNotification(
315 const Status notification_status,
316 const std::u16string& title,
317 const std::u16string& message) {
318 PA_LOG(VERBOSE) << "Showing "
319 << GetNotificationDescriptionForLogging(notification_status)
321 base::UmaHistogramEnumeration(
322 "MultiDeviceSetup_NotificationShown",
323 GetMetricValueForNotification(notification_status));
325 ShowNotification(kSetupNotificationId, title, message,
326 message_center::RichNotificationData());
327 notification_status_ = notification_status;
330 void MultiDeviceNotificationPresenter::ShowNotification(
331 const std::string& id,
332 const std::u16string& title,
333 const std::u16string& message,
334 message_center::RichNotificationData optional_fields) {
335 if (g_disable_notifications_for_test_)
338 std::unique_ptr<message_center::Notification> notification =
339 CreateSystemNotificationPtr(
340 message_center::NotificationType::NOTIFICATION_TYPE_SIMPLE, id, title,
341 message, std::u16string() /* display_source */,
342 GURL() /* origin_url */,
343 message_center::NotifierId(
344 message_center::NotifierType::SYSTEM_COMPONENT,
345 kNotifierMultiDevice, NotificationCatalogName::kMultiDevice),
346 optional_fields, nullptr /* delegate */,
347 kNotificationMultiDeviceSetupIcon,
348 message_center::SystemNotificationWarningLevel::NORMAL);
350 if (message_center_->FindVisibleNotificationById(kSetupNotificationId)) {
351 message_center_->UpdateNotification(id, std::move(notification));
355 message_center_->AddNotification(std::move(notification));
358 void MultiDeviceNotificationPresenter::FlushForTesting() {
359 if (multidevice_setup_remote_)
360 multidevice_setup_remote_.FlushForTesting();