1 // Copyright (c) 2013 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.
4
5 #include "ui/message_center/message_center_impl.h"
6
7 #include <algorithm>
8 #include <memory>
9 #include <utility>
10 #include <vector>
11
12 #include "base/auto_reset.h"
13 #include "base/bind.h"
14 #include "base/command_line.h"
15 #include "base/macros.h"
16 #include "base/observer_list.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string_util.h"
19 #include "build/build_config.h"
20 #include "ui/message_center/lock_screen/lock_screen_controller.h"
21 #include "ui/message_center/message_center_types.h"
22 #include "ui/message_center/notification_blocker.h"
23 #include "ui/message_center/notification_list.h"
24 #include "ui/message_center/popup_timers_controller.h"
25 #include "ui/message_center/public/cpp/message_center_constants.h"
26 #include "ui/message_center/public/cpp/notification.h"
27 #include "ui/message_center/public/cpp/notification_types.h"
28
29 namespace message_center {
30
31 ////////////////////////////////////////////////////////////////////////////////
32 // MessageCenterImpl
33
MessageCenterImpl(std::unique_ptr<LockScreenController> lock_screen_controller)34 MessageCenterImpl::MessageCenterImpl(
35 std::unique_ptr<LockScreenController> lock_screen_controller)
36 : MessageCenter(),
37 lock_screen_controller_(std::move(lock_screen_controller)),
38 popup_timers_controller_(std::make_unique<PopupTimersController>(this)),
39 stats_collector_(this) {
40 notification_list_ = std::make_unique<NotificationList>(this);
41 }
42
~MessageCenterImpl()43 MessageCenterImpl::~MessageCenterImpl() {
44 }
45
AddObserver(MessageCenterObserver * observer)46 void MessageCenterImpl::AddObserver(MessageCenterObserver* observer) {
47 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
48 observer_list_.AddObserver(observer);
49 }
50
RemoveObserver(MessageCenterObserver * observer)51 void MessageCenterImpl::RemoveObserver(MessageCenterObserver* observer) {
52 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
53 observer_list_.RemoveObserver(observer);
54 }
55
AddNotificationBlocker(NotificationBlocker * blocker)56 void MessageCenterImpl::AddNotificationBlocker(NotificationBlocker* blocker) {
57 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
58 if (base::Contains(blockers_, blocker))
59 return;
60
61 blocker->AddObserver(this);
62 blockers_.push_back(blocker);
63 }
64
RemoveNotificationBlocker(NotificationBlocker * blocker)65 void MessageCenterImpl::RemoveNotificationBlocker(
66 NotificationBlocker* blocker) {
67 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
68 auto iter = std::find(blockers_.begin(), blockers_.end(), blocker);
69 if (iter == blockers_.end())
70 return;
71 blocker->RemoveObserver(this);
72 blockers_.erase(iter);
73 }
74
OnBlockingStateChanged(NotificationBlocker * blocker)75 void MessageCenterImpl::OnBlockingStateChanged(NotificationBlocker* blocker) {
76 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
77 std::list<std::string> blocked;
78 NotificationList::PopupNotifications popups =
79 notification_list_->GetPopupNotifications(blockers_, &blocked);
80
81 visible_notifications_ =
82 notification_list_->GetVisibleNotifications(blockers_);
83
84 for (const std::string& notification_id : blocked) {
85 for (auto& observer : observer_list_)
86 observer.OnNotificationUpdated(notification_id);
87 }
88 for (auto& observer : observer_list_)
89 observer.OnBlockingStateChanged(blocker);
90 }
91
SetVisibility(Visibility visibility)92 void MessageCenterImpl::SetVisibility(Visibility visibility) {
93 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
94 visible_ = (visibility == VISIBILITY_MESSAGE_CENTER);
95
96 if (visible_) {
97 std::set<std::string> updated_ids;
98 notification_list_->SetNotificationsShown(blockers_, &updated_ids);
99
100 for (const auto& id : updated_ids) {
101 for (auto& observer : observer_list_)
102 observer.OnNotificationUpdated(id);
103 }
104
105 for (Notification* notification : GetPopupNotifications())
106 MarkSinglePopupAsShown(notification->id(), false);
107 }
108
109 for (auto& observer : observer_list_)
110 observer.OnCenterVisibilityChanged(visibility);
111 }
112
IsMessageCenterVisible() const113 bool MessageCenterImpl::IsMessageCenterVisible() const {
114 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
115 return visible_;
116 }
117
SetHasMessageCenterView(bool has_message_center_view)118 void MessageCenterImpl::SetHasMessageCenterView(bool has_message_center_view) {
119 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
120 has_message_center_view_ = has_message_center_view;
121 }
122
HasMessageCenterView() const123 bool MessageCenterImpl::HasMessageCenterView() const {
124 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
125 return has_message_center_view_;
126 }
127
NotificationCount() const128 size_t MessageCenterImpl::NotificationCount() const {
129 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
130 return visible_notifications_.size();
131 }
132
HasPopupNotifications() const133 bool MessageCenterImpl::HasPopupNotifications() const {
134 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
135 return !IsMessageCenterVisible() &&
136 notification_list_->HasPopupNotifications(blockers_);
137 }
138
IsQuietMode() const139 bool MessageCenterImpl::IsQuietMode() const {
140 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
141 return notification_list_->quiet_mode();
142 }
143
IsSpokenFeedbackEnabled() const144 bool MessageCenterImpl::IsSpokenFeedbackEnabled() const {
145 return spoken_feedback_enabled_;
146 }
147
FindVisibleNotificationById(const std::string & id)148 Notification* MessageCenterImpl::FindVisibleNotificationById(
149 const std::string& id) {
150 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
151
152 const auto& notifications = GetVisibleNotifications();
153 for (auto* notification : notifications) {
154 if (notification->id() == id)
155 return notification;
156 }
157
158 return nullptr;
159 }
160
FindNotificationsByAppId(const std::string & app_id)161 NotificationList::Notifications MessageCenterImpl::FindNotificationsByAppId(
162 const std::string& app_id) {
163 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
164 return notification_list_->GetNotificationsByAppId(app_id);
165 }
166
167 const NotificationList::Notifications&
GetVisibleNotifications()168 MessageCenterImpl::GetVisibleNotifications() {
169 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
170 return visible_notifications_;
171 }
172
173 NotificationList::PopupNotifications
GetPopupNotifications()174 MessageCenterImpl::GetPopupNotifications() {
175 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
176 return notification_list_->GetPopupNotifications(blockers_, nullptr);
177 }
178
179 //------------------------------------------------------------------------------
180 // Client code interface.
AddNotification(std::unique_ptr<Notification> notification)181 void MessageCenterImpl::AddNotification(
182 std::unique_ptr<Notification> notification) {
183 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
184 DCHECK(notification);
185 const std::string id = notification->id();
186 for (size_t i = 0; i < blockers_.size(); ++i)
187 blockers_[i]->CheckState();
188
189 // Sometimes the notification can be added with the same id and the
190 // |notification_list| will replace the notification instead of adding new.
191 // This is essentially an update rather than addition.
192 bool already_exists = (notification_list_->GetNotificationById(id) != NULL);
193 if (already_exists) {
194 UpdateNotification(id, std::move(notification));
195 return;
196 }
197
198 notification_list_->AddNotification(std::move(notification));
199 visible_notifications_ =
200 notification_list_->GetVisibleNotifications(blockers_);
201 for (auto& observer : observer_list_) {
202 observer.OnNotificationAdded(id);
203 }
204 }
205
UpdateNotification(const std::string & old_id,std::unique_ptr<Notification> new_notification)206 void MessageCenterImpl::UpdateNotification(
207 const std::string& old_id,
208 std::unique_ptr<Notification> new_notification) {
209 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
210 for (size_t i = 0; i < blockers_.size(); ++i)
211 blockers_[i]->CheckState();
212
213 std::string new_id = new_notification->id();
214 notification_list_->UpdateNotificationMessage(old_id,
215 std::move(new_notification));
216 visible_notifications_ =
217 notification_list_->GetVisibleNotifications(blockers_);
218 for (auto& observer : observer_list_) {
219 if (old_id == new_id) {
220 observer.OnNotificationUpdated(new_id);
221 } else {
222 observer.OnNotificationRemoved(old_id, false);
223 observer.OnNotificationAdded(new_id);
224 }
225 }
226 }
227
RemoveNotification(const std::string & id,bool by_user)228 void MessageCenterImpl::RemoveNotification(const std::string& id,
229 bool by_user) {
230 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
231
232 Notification* notification = notification_list_->GetNotificationById(id);
233 if (!notification)
234 return;
235
236 if (by_user && notification->pinned()) {
237 // When pinned, a popup will not be removed completely but moved into the
238 // message center bubble.
239 MarkSinglePopupAsShown(id, true);
240 return;
241 }
242
243 // In many cases |id| is a reference to an existing notification instance
244 // but the instance can be destructed in this method. Hence copies the id
245 // explicitly here.
246 std::string copied_id(id);
247
248 scoped_refptr<NotificationDelegate> delegate =
249 notification_list_->GetNotificationDelegate(copied_id);
250 if (delegate.get())
251 delegate->Close(by_user);
252
253 notification_list_->RemoveNotification(copied_id);
254 visible_notifications_ =
255 notification_list_->GetVisibleNotifications(blockers_);
256 for (auto& observer : observer_list_)
257 observer.OnNotificationRemoved(copied_id, by_user);
258 }
259
RemoveNotificationsForNotifierId(const NotifierId & notifier_id)260 void MessageCenterImpl::RemoveNotificationsForNotifierId(
261 const NotifierId& notifier_id) {
262 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
263 NotificationList::Notifications notifications =
264 notification_list_->GetNotificationsByNotifierId(notifier_id);
265 for (auto* notification : notifications)
266 RemoveNotification(notification->id(), false);
267 if (!notifications.empty()) {
268 visible_notifications_ =
269 notification_list_->GetVisibleNotifications(blockers_);
270 }
271 }
272
RemoveAllNotifications(bool by_user,RemoveType type)273 void MessageCenterImpl::RemoveAllNotifications(bool by_user, RemoveType type) {
274 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
275 bool remove_pinned = (type == RemoveType::ALL);
276
277 const NotificationBlockers& blockers =
278 remove_pinned ? NotificationBlockers() /* empty blockers */
279 : blockers_; /* use default blockers */
280
281 const NotificationList::Notifications notifications =
282 notification_list_->GetVisibleNotifications(blockers);
283 std::set<std::string> ids;
284 for (auto* notification : notifications) {
285 if (!remove_pinned && notification->pinned())
286 continue;
287
288 ids.insert(notification->id());
289 scoped_refptr<NotificationDelegate> delegate = notification->delegate();
290 if (delegate.get())
291 delegate->Close(by_user);
292 notification_list_->RemoveNotification(notification->id());
293 }
294
295 if (!ids.empty()) {
296 visible_notifications_ =
297 notification_list_->GetVisibleNotifications(blockers_);
298 }
299 for (const auto& id : ids) {
300 for (auto& observer : observer_list_)
301 observer.OnNotificationRemoved(id, by_user);
302 }
303 }
304
SetNotificationIcon(const std::string & notification_id,const gfx::Image & image)305 void MessageCenterImpl::SetNotificationIcon(const std::string& notification_id,
306 const gfx::Image& image) {
307 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
308
309 if (notification_list_->SetNotificationIcon(notification_id, image)) {
310 for (auto& observer : observer_list_)
311 observer.OnNotificationUpdated(notification_id);
312 }
313 }
314
SetNotificationImage(const std::string & notification_id,const gfx::Image & image)315 void MessageCenterImpl::SetNotificationImage(const std::string& notification_id,
316 const gfx::Image& image) {
317 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
318 if (notification_list_->SetNotificationImage(notification_id, image)) {
319 for (auto& observer : observer_list_)
320 observer.OnNotificationUpdated(notification_id);
321 }
322 }
323
ClickOnNotification(const std::string & id)324 void MessageCenterImpl::ClickOnNotification(const std::string& id) {
325 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
326 if (FindVisibleNotificationById(id) == NULL)
327 return;
328
329 lock_screen_controller_->DismissLockScreenThenExecute(
330 base::BindOnce(&MessageCenterImpl::ClickOnNotificationUnlocked,
331 base::Unretained(this), id, base::nullopt, base::nullopt),
332 base::OnceClosure());
333 }
334
ClickOnNotificationButton(const std::string & id,int button_index)335 void MessageCenterImpl::ClickOnNotificationButton(const std::string& id,
336 int button_index) {
337 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
338 if (!FindVisibleNotificationById(id))
339 return;
340
341 lock_screen_controller_->DismissLockScreenThenExecute(
342 base::BindOnce(&MessageCenterImpl::ClickOnNotificationUnlocked,
343 base::Unretained(this), id, button_index, base::nullopt),
344 base::OnceClosure());
345 }
346
ClickOnNotificationButtonWithReply(const std::string & id,int button_index,const base::string16 & reply)347 void MessageCenterImpl::ClickOnNotificationButtonWithReply(
348 const std::string& id,
349 int button_index,
350 const base::string16& reply) {
351 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
352 if (!FindVisibleNotificationById(id))
353 return;
354
355 lock_screen_controller_->DismissLockScreenThenExecute(
356 base::BindOnce(&MessageCenterImpl::ClickOnNotificationUnlocked,
357 base::Unretained(this), id, button_index, reply),
358 base::OnceClosure());
359 }
360
ClickOnNotificationUnlocked(const std::string & id,const base::Optional<int> & button_index,const base::Optional<base::string16> & reply)361 void MessageCenterImpl::ClickOnNotificationUnlocked(
362 const std::string& id,
363 const base::Optional<int>& button_index,
364 const base::Optional<base::string16>& reply) {
365 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
366
367 // This method must be called under unlocked screen.
368 DCHECK(!lock_screen_controller_->IsScreenLocked());
369
370 // Ensure the notificaiton is still visible.
371 if (FindVisibleNotificationById(id) == NULL)
372 return;
373
374 if (HasMessageCenterView() && HasPopupNotifications())
375 MarkSinglePopupAsShown(id, true);
376 for (auto& observer : observer_list_)
377 observer.OnNotificationClicked(id, button_index, reply);
378
379 scoped_refptr<NotificationDelegate> delegate =
380 notification_list_->GetNotificationDelegate(id);
381 if (delegate)
382 delegate->Click(button_index, reply);
383 }
384
ClickOnSettingsButton(const std::string & id)385 void MessageCenterImpl::ClickOnSettingsButton(const std::string& id) {
386 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
387 Notification* notification = notification_list_->GetNotificationById(id);
388
389 bool handled_by_delegate =
390 notification &&
391 (notification->rich_notification_data().settings_button_handler ==
392 SettingsButtonHandler::DELEGATE);
393 if (handled_by_delegate)
394 notification->delegate()->SettingsClick();
395
396 for (auto& observer : observer_list_)
397 observer.OnNotificationSettingsClicked(handled_by_delegate);
398 }
399
DisableNotification(const std::string & id)400 void MessageCenterImpl::DisableNotification(const std::string& id) {
401 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
402 Notification* notification = notification_list_->GetNotificationById(id);
403
404 if (notification && notification->delegate()) {
405 notification->delegate()->DisableNotification();
406 RemoveNotificationsForNotifierId(notification->notifier_id());
407 }
408 }
409
MarkSinglePopupAsShown(const std::string & id,bool mark_notification_as_read)410 void MessageCenterImpl::MarkSinglePopupAsShown(const std::string& id,
411 bool mark_notification_as_read) {
412 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
413 if (FindVisibleNotificationById(id) == NULL)
414 return;
415
416 if (HasMessageCenterView()) {
417 notification_list_->MarkSinglePopupAsShown(id, mark_notification_as_read);
418 for (auto& observer : observer_list_) {
419 observer.OnNotificationUpdated(id);
420 observer.OnNotificationPopupShown(id, mark_notification_as_read);
421 }
422 } else {
423 RemoveNotification(id, false);
424 }
425 }
426
DisplayedNotification(const std::string & id,const DisplaySource source)427 void MessageCenterImpl::DisplayedNotification(
428 const std::string& id,
429 const DisplaySource source) {
430 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
431
432 // This method may be called from the handlers, so we shouldn't manipulate
433 // notifications in this method.
434
435 if (FindVisibleNotificationById(id) == NULL)
436 return;
437
438 if (HasPopupNotifications())
439 notification_list_->MarkSinglePopupAsDisplayed(id);
440 scoped_refptr<NotificationDelegate> delegate =
441 notification_list_->GetNotificationDelegate(id);
442 for (auto& observer : observer_list_)
443 observer.OnNotificationDisplayed(id, source);
444 }
445
SetQuietMode(bool in_quiet_mode)446 void MessageCenterImpl::SetQuietMode(bool in_quiet_mode) {
447 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
448 if (in_quiet_mode != notification_list_->quiet_mode()) {
449 notification_list_->SetQuietMode(in_quiet_mode);
450 for (auto& observer : observer_list_)
451 observer.OnQuietModeChanged(in_quiet_mode);
452 }
453 quiet_mode_timer_.reset();
454 }
455
SetSpokenFeedbackEnabled(bool enabled)456 void MessageCenterImpl::SetSpokenFeedbackEnabled(bool enabled) {
457 spoken_feedback_enabled_ = enabled;
458 }
459
EnterQuietModeWithExpire(const base::TimeDelta & expires_in)460 void MessageCenterImpl::EnterQuietModeWithExpire(
461 const base::TimeDelta& expires_in) {
462 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
463 if (quiet_mode_timer_) {
464 // Note that the capital Reset() is the method to restart the timer, not
465 // scoped_ptr::reset().
466 quiet_mode_timer_->Reset();
467 } else {
468 notification_list_->SetQuietMode(true);
469 for (auto& observer : observer_list_)
470 observer.OnQuietModeChanged(true);
471
472 quiet_mode_timer_ = std::make_unique<base::OneShotTimer>();
473 quiet_mode_timer_->Start(FROM_HERE, expires_in,
474 base::BindOnce(&MessageCenterImpl::SetQuietMode,
475 base::Unretained(this), false));
476 }
477 }
478
RestartPopupTimers()479 void MessageCenterImpl::RestartPopupTimers() {
480 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
481 if (popup_timers_controller_)
482 popup_timers_controller_->StartAll();
483 }
484
PausePopupTimers()485 void MessageCenterImpl::PausePopupTimers() {
486 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
487 if (popup_timers_controller_)
488 popup_timers_controller_->PauseAll();
489 }
490
GetSystemNotificationAppName() const491 const base::string16& MessageCenterImpl::GetSystemNotificationAppName() const {
492 return system_notification_app_name_;
493 }
494
SetSystemNotificationAppName(const base::string16 & name)495 void MessageCenterImpl::SetSystemNotificationAppName(
496 const base::string16& name) {
497 system_notification_app_name_ = name;
498 }
499
DisableTimersForTest()500 void MessageCenterImpl::DisableTimersForTest() {
501 popup_timers_controller_.reset();
502 }
503
504 } // namespace message_center
505