1 // Copyright 2018 The Chromium Authors. All rights reserved.
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/public/cpp/notification_utils.h"
11 #include "ash/public/cpp/system_tray_client.h"
12 #include "ash/resources/vector_icons/vector_icons.h"
13 #include "ash/session/session_controller_impl.h"
14 #include "ash/shell.h"
15 #include "ash/shell_delegate.h"
16 #include "ash/strings/grit/ash_strings.h"
17 #include "ash/system/model/system_tray_model.h"
18 #include "base/bind_helpers.h"
19 #include "base/memory/ptr_util.h"
20 #include "base/metrics/histogram_macros.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "chromeos/components/multidevice/logging/logging.h"
23 #include "ui/base/l10n/l10n_util.h"
24 #include "ui/chromeos/devicetype_utils.h"
25 #include "ui/message_center/message_center.h"
26 #include "ui/message_center/public/cpp/notification_types.h"
27 #include "ui/message_center/public/cpp/notifier_id.h"
33 const char kNotifierMultiDevice[] = "ash.multi_device_setup";
38 const char MultiDeviceNotificationPresenter::kNotificationId[] =
39 "cros_multi_device_setup_notification_id";
43 MultiDeviceNotificationPresenter::GetNotificationDescriptionForLogging(
44 Status notification_status) {
45 switch (notification_status) {
46 case Status::kNewUserNotificationVisible:
47 return "notification to prompt setup";
48 case Status::kExistingUserHostSwitchedNotificationVisible:
49 return "notification of switch to new host";
50 case Status::kExistingUserNewChromebookNotificationVisible:
51 return "notification of new Chromebook added";
52 case Status::kNoNotificationVisible:
53 return "no notification";
59 MultiDeviceNotificationPresenter::NotificationType
60 MultiDeviceNotificationPresenter::GetMetricValueForNotification(
61 Status notification_status) {
62 switch (notification_status) {
63 case Status::kNewUserNotificationVisible:
64 return kNotificationTypeNewUserPotentialHostExists;
65 case Status::kExistingUserHostSwitchedNotificationVisible:
66 return kNotificationTypeExistingUserHostSwitched;
67 case Status::kExistingUserNewChromebookNotificationVisible:
68 return kNotificationTypeExistingUserNewChromebookAdded;
69 case Status::kNoNotificationVisible:
71 return kNotificationTypeMax;
75 MultiDeviceNotificationPresenter::MultiDeviceNotificationPresenter(
76 message_center::MessageCenter* message_center)
77 : message_center_(message_center) {
78 DCHECK(message_center_);
80 Shell::Get()->session_controller()->AddObserver(this);
82 // If the constructor is called after the session state has already been set
83 // (e.g., if recovering from a crash), handle that now. If the user has not
84 // yet logged in, this will be a no-op.
85 ObserveMultiDeviceSetupIfPossible();
88 MultiDeviceNotificationPresenter::~MultiDeviceNotificationPresenter() {
89 message_center_->RemoveObserver(this);
90 Shell::Get()->session_controller()->RemoveObserver(this);
93 void MultiDeviceNotificationPresenter::OnPotentialHostExistsForNewUser() {
94 base::string16 title = l10n_util::GetStringUTF16(
95 IDS_ASH_MULTI_DEVICE_SETUP_NEW_USER_POTENTIAL_HOST_EXISTS_TITLE);
96 base::string16 message = l10n_util::GetStringFUTF16(
97 IDS_ASH_MULTI_DEVICE_SETUP_NEW_USER_POTENTIAL_HOST_EXISTS_MESSAGE,
98 ui::GetChromeOSDeviceName());
99 ShowNotification(Status::kNewUserNotificationVisible, title, message);
102 void MultiDeviceNotificationPresenter::OnNoLongerNewUser() {
103 if (notification_status_ != Status::kNewUserNotificationVisible)
105 RemoveMultiDeviceSetupNotification();
108 void MultiDeviceNotificationPresenter::OnConnectedHostSwitchedForExistingUser(
109 const std::string& new_host_device_name) {
110 base::string16 title = l10n_util::GetStringFUTF16(
111 IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_HOST_SWITCHED_TITLE,
112 base::ASCIIToUTF16(new_host_device_name));
113 base::string16 message = l10n_util::GetStringFUTF16(
114 IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_HOST_SWITCHED_MESSAGE,
115 ui::GetChromeOSDeviceName());
116 ShowNotification(Status::kExistingUserHostSwitchedNotificationVisible, title,
120 void MultiDeviceNotificationPresenter::OnNewChromebookAddedForExistingUser(
121 const std::string& new_host_device_name) {
122 base::string16 title = l10n_util::GetStringFUTF16(
123 IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_NEW_CHROME_DEVICE_ADDED_TITLE,
124 base::ASCIIToUTF16(new_host_device_name));
125 base::string16 message = l10n_util::GetStringFUTF16(
126 IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_NEW_CHROME_DEVICE_ADDED_MESSAGE,
127 ui::GetChromeOSDeviceName());
128 ShowNotification(Status::kExistingUserNewChromebookNotificationVisible, title,
132 void MultiDeviceNotificationPresenter::RemoveMultiDeviceSetupNotification() {
133 notification_status_ = Status::kNoNotificationVisible;
134 message_center_->RemoveNotification(kNotificationId,
135 /* by_user */ false);
138 void MultiDeviceNotificationPresenter::OnUserSessionAdded(
139 const AccountId& account_id) {
140 ObserveMultiDeviceSetupIfPossible();
143 void MultiDeviceNotificationPresenter::OnSessionStateChanged(
144 session_manager::SessionState state) {
145 ObserveMultiDeviceSetupIfPossible();
148 void MultiDeviceNotificationPresenter::OnNotificationRemoved(
149 const std::string& notification_id,
151 if (by_user && notification_id == kNotificationId) {
152 UMA_HISTOGRAM_ENUMERATION(
153 "MultiDeviceSetup_NotificationDismissed",
154 GetMetricValueForNotification(notification_status_),
155 kNotificationTypeMax);
159 void MultiDeviceNotificationPresenter::OnNotificationClicked(
160 const std::string& notification_id,
161 const base::Optional<int>& button_index,
162 const base::Optional<base::string16>& reply) {
163 if (notification_id != kNotificationId)
166 DCHECK(notification_status_ != Status::kNoNotificationVisible);
167 PA_LOG(VERBOSE) << "User clicked "
168 << GetNotificationDescriptionForLogging(notification_status_)
170 UMA_HISTOGRAM_ENUMERATION("MultiDeviceSetup_NotificationClicked",
171 GetMetricValueForNotification(notification_status_),
172 kNotificationTypeMax);
173 switch (notification_status_) {
174 case Status::kNewUserNotificationVisible:
175 Shell::Get()->system_tray_model()->client()->ShowMultiDeviceSetup();
177 case Status::kExistingUserHostSwitchedNotificationVisible:
178 // Clicks on the 'host switched' and 'Chromebook added' notifications have
179 // the same effect, i.e. opening the Settings subpage.
181 case Status::kExistingUserNewChromebookNotificationVisible:
183 ->system_tray_model()
185 ->ShowConnectedDevicesSettings();
187 case Status::kNoNotificationVisible:
190 RemoveMultiDeviceSetupNotification();
193 void MultiDeviceNotificationPresenter::ObserveMultiDeviceSetupIfPossible() {
194 // If already the delegate, there is nothing else to do.
195 if (multidevice_setup_remote_)
198 const SessionControllerImpl* session_controller =
199 Shell::Get()->session_controller();
201 if (session_controller->GetSessionState() !=
202 session_manager::SessionState::ACTIVE) {
206 const UserSession* user_session = session_controller->GetPrimaryUserSession();
208 // The primary user session may be unavailable (e.g., for test/guest users).
212 Shell::Get()->shell_delegate()->BindMultiDeviceSetup(
213 multidevice_setup_remote_.BindNewPipeAndPassReceiver());
215 // Add this object as the delegate of the MultiDeviceSetup Service.
216 multidevice_setup_remote_->SetAccountStatusChangeDelegate(
217 receiver_.BindNewPipeAndPassRemote());
219 message_center_->AddObserver(this);
222 void MultiDeviceNotificationPresenter::ShowNotification(
223 const Status notification_status,
224 const base::string16& title,
225 const base::string16& message) {
226 PA_LOG(VERBOSE) << "Showing "
227 << GetNotificationDescriptionForLogging(notification_status)
229 UMA_HISTOGRAM_ENUMERATION("MultiDeviceSetup_NotificationShown",
230 GetMetricValueForNotification(notification_status),
231 kNotificationTypeMax);
232 if (message_center_->FindVisibleNotificationById(kNotificationId)) {
233 message_center_->UpdateNotification(kNotificationId,
234 CreateNotification(title, message));
236 message_center_->AddNotification(CreateNotification(title, message));
238 notification_status_ = notification_status;
241 std::unique_ptr<message_center::Notification>
242 MultiDeviceNotificationPresenter::CreateNotification(
243 const base::string16& title,
244 const base::string16& message) {
245 return CreateSystemNotification(
246 message_center::NotificationType::NOTIFICATION_TYPE_SIMPLE,
247 kNotificationId, title, message, base::string16() /* display_source */,
248 GURL() /* origin_url */,
249 message_center::NotifierId(message_center::NotifierType::SYSTEM_COMPONENT,
250 kNotifierMultiDevice),
251 message_center::RichNotificationData(), nullptr /* delegate */,
252 kNotificationMultiDeviceSetupIcon,
253 message_center::SystemNotificationWarningLevel::NORMAL);
256 void MultiDeviceNotificationPresenter::FlushForTesting() {
257 if (multidevice_setup_remote_)
258 multidevice_setup_remote_.FlushForTesting();