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/vector_icons/vector_icons.h"
11 #include "ash/public/interfaces/session_controller.mojom.h"
12 #include "ash/session/session_controller.h"
13 #include "ash/shell.h"
14 #include "ash/strings/grit/ash_strings.h"
15 #include "ash/system/model/system_tray_model.h"
16 #include "base/bind_helpers.h"
17 #include "base/memory/ptr_util.h"
18 #include "base/metrics/histogram_macros.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "chromeos/components/proximity_auth/logging/logging.h"
21 #include "chromeos/services/multidevice_setup/public/mojom/constants.mojom.h"
22 #include "services/service_manager/public/cpp/connector.h"
23 #include "ui/base/l10n/l10n_util.h"
24 #include "ui/message_center/message_center.h"
25 #include "ui/message_center/public/cpp/notification_types.h"
26 #include "ui/message_center/public/cpp/notifier_id.h"
32 const char kNotifierMultiDevice[] = "ash.multi_device_setup";
37 const char MultiDeviceNotificationPresenter::kNotificationId[] =
38 "cros_multi_device_setup_notification_id";
42 MultiDeviceNotificationPresenter::GetNotificationDescriptionForLogging(
43 Status notification_status) {
44 switch (notification_status) {
45 case Status::kNewUserNotificationVisible:
46 return "notification to prompt setup";
47 case Status::kExistingUserHostSwitchedNotificationVisible:
48 return "notification of switch to new host";
49 case Status::kExistingUserNewChromebookNotificationVisible:
50 return "notification of new Chromebook added";
51 case Status::kNoNotificationVisible:
52 return "no notification";
57 MultiDeviceNotificationPresenter::OpenUiDelegate::~OpenUiDelegate() = default;
59 void MultiDeviceNotificationPresenter::OpenUiDelegate::
60 OpenMultiDeviceSetupUi() {
61 Shell::Get()->system_tray_model()->client_ptr()->ShowMultiDeviceSetup();
64 void MultiDeviceNotificationPresenter::OpenUiDelegate::
65 OpenConnectedDevicesSettings() {
69 ->ShowConnectedDevicesSettings();
73 MultiDeviceNotificationPresenter::NotificationType
74 MultiDeviceNotificationPresenter::GetMetricValueForNotification(
75 Status notification_status) {
76 switch (notification_status) {
77 case Status::kNewUserNotificationVisible:
78 return kNotificationTypeNewUserPotentialHostExists;
79 case Status::kExistingUserHostSwitchedNotificationVisible:
80 return kNotificationTypeExistingUserHostSwitched;
81 case Status::kExistingUserNewChromebookNotificationVisible:
82 return kNotificationTypeExistingUserNewChromebookAdded;
83 case Status::kNoNotificationVisible:
85 return kNotificationTypeMax;
89 MultiDeviceNotificationPresenter::MultiDeviceNotificationPresenter(
90 message_center::MessageCenter* message_center,
91 service_manager::Connector* connector)
92 : message_center_(message_center),
93 connector_(connector),
95 open_ui_delegate_(std::make_unique<OpenUiDelegate>()),
96 weak_ptr_factory_(this) {
97 DCHECK(message_center_);
100 Shell::Get()->session_controller()->AddObserver(this);
102 // If the constructor is called after the session state has already been set
103 // (e.g., if recovering from a crash), handle that now. If the user has not
104 // yet logged in, this will be a no-op.
105 ObserveMultiDeviceSetupIfPossible();
108 MultiDeviceNotificationPresenter::~MultiDeviceNotificationPresenter() {
109 Shell::Get()->session_controller()->RemoveObserver(this);
112 void MultiDeviceNotificationPresenter::OnPotentialHostExistsForNewUser() {
113 base::string16 title = l10n_util::GetStringUTF16(
114 IDS_ASH_MULTI_DEVICE_SETUP_NEW_USER_POTENTIAL_HOST_EXISTS_TITLE);
115 base::string16 message = l10n_util::GetStringUTF16(
116 IDS_ASH_MULTI_DEVICE_SETUP_NEW_USER_POTENTIAL_HOST_EXISTS_MESSAGE);
117 ShowNotification(Status::kNewUserNotificationVisible, title, message);
120 void MultiDeviceNotificationPresenter::OnNoLongerNewUser() {
121 if (notification_status_ != Status::kNewUserNotificationVisible)
123 RemoveMultiDeviceSetupNotification();
126 void MultiDeviceNotificationPresenter::OnConnectedHostSwitchedForExistingUser(
127 const std::string& new_host_device_name) {
128 base::string16 title = l10n_util::GetStringFUTF16(
129 IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_HOST_SWITCHED_TITLE,
130 base::ASCIIToUTF16(new_host_device_name));
131 base::string16 message = l10n_util::GetStringUTF16(
132 IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_HOST_SWITCHED_MESSAGE);
133 ShowNotification(Status::kExistingUserHostSwitchedNotificationVisible, title,
137 void MultiDeviceNotificationPresenter::OnNewChromebookAddedForExistingUser(
138 const std::string& new_host_device_name) {
139 base::string16 title = l10n_util::GetStringFUTF16(
140 IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_NEW_CHROMEBOOK_ADDED_TITLE,
141 base::ASCIIToUTF16(new_host_device_name));
142 base::string16 message = l10n_util::GetStringUTF16(
143 IDS_ASH_MULTI_DEVICE_SETUP_EXISTING_USER_NEW_CHROMEBOOK_ADDED_MESSAGE);
144 ShowNotification(Status::kExistingUserNewChromebookNotificationVisible, title,
148 void MultiDeviceNotificationPresenter::RemoveMultiDeviceSetupNotification() {
149 notification_status_ = Status::kNoNotificationVisible;
150 message_center_->RemoveNotification(kNotificationId,
151 /* by_user */ false);
154 void MultiDeviceNotificationPresenter::OnUserSessionAdded(
155 const AccountId& account_id) {
156 ObserveMultiDeviceSetupIfPossible();
159 void MultiDeviceNotificationPresenter::OnSessionStateChanged(
160 session_manager::SessionState state) {
161 ObserveMultiDeviceSetupIfPossible();
164 void MultiDeviceNotificationPresenter::ObserveMultiDeviceSetupIfPossible() {
165 // If already the delegate, there is nothing else to do.
166 if (multidevice_setup_ptr_)
169 const SessionController* session_controller =
170 Shell::Get()->session_controller();
172 // If no active user is logged in, there is nothing to do.
173 if (session_controller->GetSessionState() !=
174 session_manager::SessionState::ACTIVE) {
178 const mojom::UserSession* user_session =
179 session_controller->GetPrimaryUserSession();
181 // The primary user session may be unavailable (e.g., for test/guest users).
185 std::string service_user_id = user_session->user_info->service_user_id;
186 DCHECK(!service_user_id.empty());
188 connector_->BindInterface(
189 service_manager::Identity(
190 chromeos::multidevice_setup::mojom::kServiceName, service_user_id),
191 &multidevice_setup_ptr_);
193 // Add this object as the delegate of the MultiDeviceSetup Service.
194 chromeos::multidevice_setup::mojom::AccountStatusChangeDelegatePtr
196 binding_.Bind(mojo::MakeRequest(&delegate_ptr));
197 multidevice_setup_ptr_->SetAccountStatusChangeDelegate(
198 std::move(delegate_ptr));
201 void MultiDeviceNotificationPresenter::OnNotificationClicked() {
202 DCHECK(notification_status_ != Status::kNoNotificationVisible);
203 PA_LOG(INFO) << "User clicked "
204 << GetNotificationDescriptionForLogging(notification_status_)
206 UMA_HISTOGRAM_ENUMERATION("MultiDeviceSetup_NotificationClicked",
207 GetMetricValueForNotification(notification_status_),
208 kNotificationTypeMax);
209 switch (notification_status_) {
210 case Status::kNewUserNotificationVisible:
211 open_ui_delegate_->OpenMultiDeviceSetupUi();
213 case Status::kExistingUserHostSwitchedNotificationVisible:
214 // Clicks on the 'host switched' and 'Chromebook added' notifications have
215 // the same effect, i.e. opening the Settings subpage.
217 case Status::kExistingUserNewChromebookNotificationVisible:
218 open_ui_delegate_->OpenConnectedDevicesSettings();
220 case Status::kNoNotificationVisible:
223 RemoveMultiDeviceSetupNotification();
226 void MultiDeviceNotificationPresenter::ShowNotification(
227 const Status notification_status,
228 const base::string16& title,
229 const base::string16& message) {
230 PA_LOG(INFO) << "Showing "
231 << GetNotificationDescriptionForLogging(notification_status)
233 UMA_HISTOGRAM_ENUMERATION("MultiDeviceSetup_NotificationShown",
234 GetMetricValueForNotification(notification_status),
235 kNotificationTypeMax);
236 if (message_center_->FindVisibleNotificationById(kNotificationId)) {
237 message_center_->UpdateNotification(kNotificationId,
238 CreateNotification(title, message));
240 message_center_->AddNotification(CreateNotification(title, message));
242 notification_status_ = notification_status;
245 std::unique_ptr<message_center::Notification>
246 MultiDeviceNotificationPresenter::CreateNotification(
247 const base::string16& title,
248 const base::string16& message) {
249 return message_center::Notification::CreateSystemNotification(
250 message_center::NotificationType::NOTIFICATION_TYPE_SIMPLE,
251 kNotificationId, title, message, base::string16() /* display_source */,
252 GURL() /* origin_url */,
253 message_center::NotifierId(
254 message_center::NotifierId::NotifierType::SYSTEM_COMPONENT,
255 kNotifierMultiDevice),
256 message_center::RichNotificationData(),
257 new message_center::HandleNotificationClickDelegate(base::BindRepeating(
258 &MultiDeviceNotificationPresenter::OnNotificationClicked,
259 weak_ptr_factory_.GetWeakPtr())),
260 ash::kNotificationMultiDeviceSetupIcon,
261 message_center::SystemNotificationWarningLevel::NORMAL);
264 void MultiDeviceNotificationPresenter::FlushForTesting() {
265 if (multidevice_setup_ptr_)
266 multidevice_setup_ptr_.FlushForTesting();