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