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/notifier_catalogs.h"
11 #include "ash/public/cpp/notification_utils.h"
12 #include "ash/public/cpp/system_tray_client.h"
13 #include "ash/resources/vector_icons/vector_icons.h"
14 #include "ash/session/session_controller_impl.h"
15 #include "ash/shell.h"
16 #include "ash/shell_delegate.h"
17 #include "ash/strings/grit/ash_strings.h"
18 #include "ash/system/model/system_tray_model.h"
19 #include "base/functional/callback_helpers.h"
20 #include "base/memory/ptr_util.h"
21 #include "base/metrics/histogram_functions.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "chromeos/ash/components/multidevice/logging/logging.h"
24 #include "ui/base/l10n/l10n_util.h"
25 #include "ui/chromeos/devicetype_utils.h"
26 #include "ui/message_center/message_center.h"
27 #include "ui/message_center/public/cpp/notifier_id.h"
33 bool g_disable_notifications_for_test_ = false;
35 const char kNotifierMultiDevice[] = "ash.multi_device_setup";
40 const char MultiDeviceNotificationPresenter::kSetupNotificationId[] =
41 "cros_multi_device_setup_notification_id";
44 const char MultiDeviceNotificationPresenter::kWifiSyncNotificationId[] =
45 "cros_wifi_sync_announcement_notification_id";
49 MultiDeviceNotificationPresenter::GetNotificationDescriptionForLogging(
50 Status notification_status) {
51 switch (notification_status) {
52 case Status::kNewUserNotificationVisible:
53 return "notification to prompt setup";
54 case Status::kExistingUserHostSwitchedNotificationVisible:
55 return "notification of switch to new host";
56 case Status::kExistingUserNewChromebookNotificationVisible:
57 return "notification of new Chromebook added";
58 case Status::kNoNotificationVisible:
59 return "no notification";
65 MultiDeviceNotificationPresenter::NotificationType
66 MultiDeviceNotificationPresenter::GetMetricValueForNotification(
67 Status notification_status) {
68 switch (notification_status) {
69 case Status::kNewUserNotificationVisible:
70 return NotificationType::kNewUserPotentialHostExists;
71 case Status::kExistingUserHostSwitchedNotificationVisible:
72 return NotificationType::kExistingUserHostSwitched;
73 case Status::kExistingUserNewChromebookNotificationVisible:
74 return NotificationType::kExistingUserNewChromebookAdded;
75 case Status::kNoNotificationVisible:
77 return NotificationType::kErrorUnknown;
81 MultiDeviceNotificationPresenter::MultiDeviceNotificationPresenter(
82 message_center::MessageCenter* message_center)
83 : message_center_(message_center) {
84 DCHECK(message_center_);
86 Shell::Get()->session_controller()->AddObserver(this);
88 // If the constructor is called after the session state has already been set
89 // (e.g., if recovering from a crash), handle that now. If the user has not
90 // yet logged in, this will be a no-op.
91 ObserveMultiDeviceSetupIfPossible();
94 MultiDeviceNotificationPresenter::~MultiDeviceNotificationPresenter() {
95 message_center_->RemoveObserver(this);
96 Shell::Get()->session_controller()->RemoveObserver(this);
99 void MultiDeviceNotificationPresenter::OnPotentialHostExistsForNewUser() {
100 std::u16string title = l10n_util::GetStringUTF16(
101 IDS_ASH_MULTI_DEVICE_SETUP_NEW_USER_POTENTIAL_HOST_EXISTS_TITLE);
102 std::u16string message = l10n_util::GetStringFUTF16(
103 IDS_ASH_MULTI_DEVICE_SETUP_NEW_USER_POTENTIAL_HOST_EXISTS_MESSAGE,
104 ui::GetChromeOSDeviceName());
105 ShowSetupNotification(Status::kNewUserNotificationVisible, title, message);
108 void MultiDeviceNotificationPresenter::OnNoLongerNewUser() {
109 if (notification_status_ != Status::kNewUserNotificationVisible)
111 RemoveMultiDeviceSetupNotification();
114 void MultiDeviceNotificationPresenter::OnConnectedHostSwitchedForExistingUser(
115 const std::string& new_host_device_name) {
116 std::u16string title = l10n_util::GetStringFUTF16(
117 IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_HOST_SWITCHED_TITLE,
118 base::ASCIIToUTF16(new_host_device_name));
119 std::u16string message = l10n_util::GetStringFUTF16(
120 IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_HOST_SWITCHED_MESSAGE,
121 ui::GetChromeOSDeviceName());
122 ShowSetupNotification(Status::kExistingUserHostSwitchedNotificationVisible,
126 void MultiDeviceNotificationPresenter::OnNewChromebookAddedForExistingUser(
127 const std::string& new_host_device_name) {
128 std::u16string title = l10n_util::GetStringFUTF16(
129 IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_NEW_CHROME_DEVICE_ADDED_TITLE,
130 base::ASCIIToUTF16(new_host_device_name));
131 std::u16string message = l10n_util::GetStringFUTF16(
132 IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_NEW_CHROME_DEVICE_ADDED_MESSAGE,
133 ui::GetChromeOSDeviceName());
134 ShowSetupNotification(Status::kExistingUserNewChromebookNotificationVisible,
138 void MultiDeviceNotificationPresenter::OnBecameEligibleForWifiSync() {
139 std::u16string title =
140 l10n_util::GetStringUTF16(IDS_ASH_MULTI_DEVICE_WIFI_SYNC_AVAILABLE_TITLE);
141 std::u16string message = l10n_util::GetStringFUTF16(
142 IDS_ASH_MULTI_DEVICE_WIFI_SYNC_AVAILABLE_MESSAGE,
143 ui::GetChromeOSDeviceName());
144 message_center::RichNotificationData optional_fields;
145 optional_fields.buttons.push_back(
146 message_center::ButtonInfo(l10n_util::GetStringUTF16(
147 IDS_ASH_MULTI_DEVICE_WIFI_SYNC_AVAILABLE_TURN_ON_BUTTON)));
148 optional_fields.buttons.push_back(
149 message_center::ButtonInfo(l10n_util::GetStringUTF16(
150 IDS_ASH_MULTI_DEVICE_WIFI_SYNC_AVAILABLE_CANCEL_BUTTON)));
152 ShowNotification(kWifiSyncNotificationId, title, message, optional_fields);
153 base::UmaHistogramEnumeration("MultiDeviceSetup_NotificationShown",
154 NotificationType::kWifiSyncAnnouncement);
158 std::unique_ptr<base::AutoReset<bool>>
159 MultiDeviceNotificationPresenter::DisableNotificationsForTesting() {
160 return std::make_unique<base::AutoReset<bool>>(
161 &g_disable_notifications_for_test_, true);
164 void MultiDeviceNotificationPresenter::RemoveMultiDeviceSetupNotification() {
165 notification_status_ = Status::kNoNotificationVisible;
166 message_center_->RemoveNotification(kSetupNotificationId,
167 /* by_user */ false);
170 void MultiDeviceNotificationPresenter::OnUserSessionAdded(
171 const AccountId& account_id) {
172 ObserveMultiDeviceSetupIfPossible();
175 void MultiDeviceNotificationPresenter::OnSessionStateChanged(
176 session_manager::SessionState state) {
177 ObserveMultiDeviceSetupIfPossible();
180 void MultiDeviceNotificationPresenter::OnNotificationRemoved(
181 const std::string& notification_id,
186 if (notification_id == kSetupNotificationId) {
187 base::UmaHistogramEnumeration(
188 "MultiDeviceSetup_NotificationDismissed",
189 GetMetricValueForNotification(notification_status_));
192 if (notification_id == kWifiSyncNotificationId) {
193 base::UmaHistogramEnumeration("MultiDeviceSetup_NotificationDismissed",
194 NotificationType::kWifiSyncAnnouncement);
199 void MultiDeviceNotificationPresenter::OnNotificationClicked(
200 const std::string& notification_id,
201 const absl::optional<int>& button_index,
202 const absl::optional<std::u16string>& reply) {
203 if (notification_id == kWifiSyncNotificationId) {
204 message_center_->RemoveNotification(kWifiSyncNotificationId,
205 /* by_user */ false);
208 switch (*button_index) {
209 case 0: // "Turn on" button
210 PA_LOG(INFO) << "Enabling Wi-Fi Sync.";
211 multidevice_setup_remote_->SetFeatureEnabledState(
212 multidevice_setup::mojom::Feature::kWifiSync,
213 /*enabled=*/true, /*auth_token=*/absl::nullopt,
214 /*callback=*/base::DoNothing());
216 case 1: // "Cancel" button
217 base::UmaHistogramEnumeration(
218 "MultiDeviceSetup_NotificationDismissed",
219 NotificationType::kWifiSyncAnnouncement);
224 Shell::Get()->system_tray_model()->client()->ShowWifiSyncSettings();
225 base::UmaHistogramEnumeration("MultiDeviceSetup_NotificationClicked",
226 NotificationType::kWifiSyncAnnouncement);
230 if (notification_id != kSetupNotificationId)
233 DCHECK(notification_status_ != Status::kNoNotificationVisible);
234 PA_LOG(VERBOSE) << "User clicked "
235 << GetNotificationDescriptionForLogging(notification_status_)
237 base::UmaHistogramEnumeration(
238 "MultiDeviceSetup_NotificationClicked",
239 GetMetricValueForNotification(notification_status_));
240 switch (notification_status_) {
241 case Status::kNewUserNotificationVisible:
242 Shell::Get()->system_tray_model()->client()->ShowMultiDeviceSetup();
244 case Status::kExistingUserHostSwitchedNotificationVisible:
245 // Clicks on the 'host switched' and 'Chromebook added' notifications have
246 // the same effect, i.e. opening the Settings subpage.
248 case Status::kExistingUserNewChromebookNotificationVisible:
250 ->system_tray_model()
252 ->ShowConnectedDevicesSettings();
254 case Status::kNoNotificationVisible:
257 RemoveMultiDeviceSetupNotification();
260 void MultiDeviceNotificationPresenter::ObserveMultiDeviceSetupIfPossible() {
261 // If already the delegate, there is nothing else to do.
262 if (multidevice_setup_remote_)
265 const SessionControllerImpl* session_controller =
266 Shell::Get()->session_controller();
268 if (session_controller->GetSessionState() !=
269 session_manager::SessionState::ACTIVE) {
273 const UserSession* user_session = session_controller->GetPrimaryUserSession();
275 // The primary user session may be unavailable (e.g., for test/guest users).
279 Shell::Get()->shell_delegate()->BindMultiDeviceSetup(
280 multidevice_setup_remote_.BindNewPipeAndPassReceiver());
282 // Add this object as the delegate of the MultiDeviceSetup Service.
283 multidevice_setup_remote_->SetAccountStatusChangeDelegate(
284 receiver_.BindNewPipeAndPassRemote());
286 message_center_->AddObserver(this);
289 void MultiDeviceNotificationPresenter::ShowSetupNotification(
290 const Status notification_status,
291 const std::u16string& title,
292 const std::u16string& message) {
293 PA_LOG(VERBOSE) << "Showing "
294 << GetNotificationDescriptionForLogging(notification_status)
296 base::UmaHistogramEnumeration(
297 "MultiDeviceSetup_NotificationShown",
298 GetMetricValueForNotification(notification_status));
300 ShowNotification(kSetupNotificationId, title, message,
301 message_center::RichNotificationData());
302 notification_status_ = notification_status;
305 void MultiDeviceNotificationPresenter::ShowNotification(
306 const std::string& id,
307 const std::u16string& title,
308 const std::u16string& message,
309 message_center::RichNotificationData optional_fields) {
310 if (g_disable_notifications_for_test_)
313 std::unique_ptr<message_center::Notification> notification =
314 CreateSystemNotificationPtr(
315 message_center::NotificationType::NOTIFICATION_TYPE_SIMPLE, id, title,
316 message, std::u16string() /* display_source */,
317 GURL() /* origin_url */,
318 message_center::NotifierId(
319 message_center::NotifierType::SYSTEM_COMPONENT,
320 kNotifierMultiDevice, NotificationCatalogName::kMultiDevice),
321 optional_fields, nullptr /* delegate */,
322 kNotificationMultiDeviceSetupIcon,
323 message_center::SystemNotificationWarningLevel::NORMAL);
325 if (message_center_->FindVisibleNotificationById(kSetupNotificationId)) {
326 message_center_->UpdateNotification(id, std::move(notification));
330 message_center_->AddNotification(std::move(notification));
333 void MultiDeviceNotificationPresenter::FlushForTesting() {
334 if (multidevice_setup_remote_)
335 multidevice_setup_remote_.FlushForTesting();