Home | History | Annotate | Download | only in message_center
      1 // Copyright (c) 2012 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/notification_list.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/logging.h"
      9 #include "base/stl_util.h"
     10 #include "base/time/time.h"
     11 #include "base/values.h"
     12 #include "ui/gfx/image/image.h"
     13 #include "ui/message_center/message_center_style.h"
     14 #include "ui/message_center/notification.h"
     15 #include "ui/message_center/notification_blocker.h"
     16 #include "ui/message_center/notification_types.h"
     17 
     18 namespace message_center {
     19 
     20 namespace {
     21 
     22 bool ShouldShowNotificationAsPopup(
     23     const Notification& notification,
     24     const NotificationBlockers& blockers) {
     25   for (size_t i = 0; i < blockers.size(); ++i) {
     26     if (!blockers[i]->ShouldShowNotificationAsPopup(notification.notifier_id()))
     27       return false;
     28   }
     29   return true;
     30 }
     31 
     32 }  // namespace
     33 
     34 bool ComparePriorityTimestampSerial::operator()(Notification* n1,
     35                                                 Notification* n2) {
     36   if (n1->priority() > n2->priority())  // Higher pri go first.
     37     return true;
     38   if (n1->priority() < n2->priority())
     39     return false;
     40   return CompareTimestampSerial()(n1, n2);
     41 }
     42 
     43 bool CompareTimestampSerial::operator()(Notification* n1, Notification* n2) {
     44   if (n1->timestamp() > n2->timestamp())  // Newer come first.
     45     return true;
     46   if (n1->timestamp() < n2->timestamp())
     47     return false;
     48   if (n1->serial_number() > n2->serial_number())  // Newer come first.
     49     return true;
     50   if (n1->serial_number() < n2->serial_number())
     51     return false;
     52   return false;
     53 }
     54 
     55 NotificationList::NotificationList()
     56     : message_center_visible_(false),
     57       quiet_mode_(false) {
     58 }
     59 
     60 NotificationList::~NotificationList() {
     61   STLDeleteContainerPointers(notifications_.begin(), notifications_.end());
     62 }
     63 
     64 void NotificationList::SetMessageCenterVisible(
     65     bool visible,
     66     std::set<std::string>* updated_ids) {
     67   if (message_center_visible_ == visible)
     68     return;
     69 
     70   message_center_visible_ = visible;
     71 
     72   if (!visible)
     73     return;
     74 
     75   for (Notifications::iterator iter = notifications_.begin();
     76        iter != notifications_.end(); ++iter) {
     77     Notification* notification = *iter;
     78     bool was_popup = notification->shown_as_popup();
     79     bool was_read = notification->IsRead();
     80     if (notification->priority() < SYSTEM_PRIORITY)
     81       notification->set_shown_as_popup(true);
     82     notification->set_is_read(true);
     83     if (updated_ids && !(was_popup && was_read))
     84       updated_ids->insert(notification->id());
     85   }
     86 }
     87 
     88 void NotificationList::AddNotification(scoped_ptr<Notification> notification) {
     89   PushNotification(notification.Pass());
     90 }
     91 
     92 void NotificationList::UpdateNotificationMessage(
     93     const std::string& old_id,
     94     scoped_ptr<Notification> new_notification) {
     95   Notifications::iterator iter = GetNotification(old_id);
     96   if (iter == notifications_.end())
     97     return;
     98 
     99   new_notification->CopyState(*iter);
    100 
    101   // Handles priority promotion. If the notification is already dismissed but
    102   // the updated notification has higher priority, it should re-appear as a
    103   // toast. Notifications coming from websites through the Web Notification API
    104   // will always re-appear on update.
    105   if ((*iter)->priority() < new_notification->priority() ||
    106       new_notification->notifier_id().type == NotifierId::WEB_PAGE) {
    107     new_notification->set_is_read(false);
    108     new_notification->set_shown_as_popup(false);
    109   }
    110 
    111   // Do not use EraseNotification and PushNotification, since we don't want to
    112   // change unread counts nor to update is_read/shown_as_popup states.
    113   Notification* old = *iter;
    114   notifications_.erase(iter);
    115   delete old;
    116 
    117   // We really don't want duplicate IDs.
    118   DCHECK(GetNotification(new_notification->id()) == notifications_.end());
    119   notifications_.insert(new_notification.release());
    120 }
    121 
    122 void NotificationList::RemoveNotification(const std::string& id) {
    123   EraseNotification(GetNotification(id));
    124 }
    125 
    126 NotificationList::Notifications NotificationList::GetNotificationsByNotifierId(
    127         const NotifierId& notifier_id) {
    128   Notifications notifications;
    129   for (Notifications::iterator iter = notifications_.begin();
    130        iter != notifications_.end(); ++iter) {
    131     if ((*iter)->notifier_id() == notifier_id)
    132       notifications.insert(*iter);
    133   }
    134   return notifications;
    135 }
    136 
    137 bool NotificationList::SetNotificationIcon(const std::string& notification_id,
    138                                            const gfx::Image& image) {
    139   Notifications::iterator iter = GetNotification(notification_id);
    140   if (iter == notifications_.end())
    141     return false;
    142   (*iter)->set_icon(image);
    143   return true;
    144 }
    145 
    146 bool NotificationList::SetNotificationImage(const std::string& notification_id,
    147                                             const gfx::Image& image) {
    148   Notifications::iterator iter = GetNotification(notification_id);
    149   if (iter == notifications_.end())
    150     return false;
    151   (*iter)->set_image(image);
    152   return true;
    153 }
    154 
    155 bool NotificationList::SetNotificationButtonIcon(
    156     const std::string& notification_id, int button_index,
    157     const gfx::Image& image) {
    158   Notifications::iterator iter = GetNotification(notification_id);
    159   if (iter == notifications_.end())
    160     return false;
    161   (*iter)->SetButtonIcon(button_index, image);
    162   return true;
    163 }
    164 
    165 bool NotificationList::HasNotificationOfType(const std::string& id,
    166                                              const NotificationType type) {
    167   Notifications::iterator iter = GetNotification(id);
    168   if (iter == notifications_.end())
    169     return false;
    170 
    171   return (*iter)->type() == type;
    172 }
    173 
    174 bool NotificationList::HasPopupNotifications(
    175     const NotificationBlockers& blockers) {
    176   for (Notifications::iterator iter = notifications_.begin();
    177        iter != notifications_.end(); ++iter) {
    178     if ((*iter)->priority() < DEFAULT_PRIORITY)
    179       break;
    180     if (!ShouldShowNotificationAsPopup(**iter, blockers))
    181       continue;
    182     if (!(*iter)->shown_as_popup())
    183       return true;
    184   }
    185   return false;
    186 }
    187 
    188 NotificationList::PopupNotifications NotificationList::GetPopupNotifications(
    189     const NotificationBlockers& blockers,
    190     std::list<std::string>* blocked_ids) {
    191   PopupNotifications result;
    192   size_t default_priority_popup_count = 0;
    193 
    194   // Collect notifications that should be shown as popups. Start from oldest.
    195   for (Notifications::const_reverse_iterator iter = notifications_.rbegin();
    196        iter != notifications_.rend(); iter++) {
    197     if ((*iter)->shown_as_popup())
    198       continue;
    199 
    200     // No popups for LOW/MIN priority.
    201     if ((*iter)->priority() < DEFAULT_PRIORITY)
    202       continue;
    203 
    204     if (!ShouldShowNotificationAsPopup(**iter, blockers)) {
    205       if (blocked_ids)
    206         blocked_ids->push_back((*iter)->id());
    207       continue;
    208     }
    209 
    210     // Checking limits. No limits for HIGH/MAX priority. DEFAULT priority
    211     // will return at most kMaxVisiblePopupNotifications entries. If the
    212     // popup entries are more, older entries are used. see crbug.com/165768
    213     if ((*iter)->priority() == DEFAULT_PRIORITY &&
    214         default_priority_popup_count++ >= kMaxVisiblePopupNotifications) {
    215       continue;
    216     }
    217 
    218     result.insert(*iter);
    219   }
    220   return result;
    221 }
    222 
    223 void NotificationList::MarkSinglePopupAsShown(
    224     const std::string& id, bool mark_notification_as_read) {
    225   Notifications::iterator iter = GetNotification(id);
    226   DCHECK(iter != notifications_.end());
    227 
    228   if ((*iter)->shown_as_popup())
    229     return;
    230 
    231   // System notification is marked as shown only when marked as read.
    232   if ((*iter)->priority() != SYSTEM_PRIORITY || mark_notification_as_read)
    233     (*iter)->set_shown_as_popup(true);
    234 
    235   // The popup notification is already marked as read when it's displayed.
    236   // Set the is_read() back to false if necessary.
    237   if (!mark_notification_as_read)
    238     (*iter)->set_is_read(false);
    239 }
    240 
    241 void NotificationList::MarkSinglePopupAsDisplayed(const std::string& id) {
    242   Notifications::iterator iter = GetNotification(id);
    243   if (iter == notifications_.end())
    244     return;
    245 
    246   if ((*iter)->shown_as_popup())
    247     return;
    248 
    249   if (!(*iter)->IsRead())
    250     (*iter)->set_is_read(true);
    251 }
    252 
    253 NotificationDelegate* NotificationList::GetNotificationDelegate(
    254     const std::string& id) {
    255   Notifications::iterator iter = GetNotification(id);
    256   if (iter == notifications_.end())
    257     return NULL;
    258   return (*iter)->delegate();
    259 }
    260 
    261 void NotificationList::SetQuietMode(bool quiet_mode) {
    262   quiet_mode_ = quiet_mode;
    263   if (quiet_mode_) {
    264     for (Notifications::iterator iter = notifications_.begin();
    265          iter != notifications_.end();
    266          ++iter) {
    267       (*iter)->set_shown_as_popup(true);
    268     }
    269   }
    270 }
    271 
    272 Notification* NotificationList::GetNotificationById(const std::string& id) {
    273   Notifications::iterator iter = GetNotification(id);
    274   if (iter != notifications_.end())
    275     return *iter;
    276   return NULL;
    277 }
    278 
    279 NotificationList::Notifications NotificationList::GetVisibleNotifications(
    280     const NotificationBlockers& blockers) const {
    281   Notifications result;
    282   for (Notifications::const_iterator iter = notifications_.begin();
    283        iter != notifications_.end(); ++iter) {
    284     bool should_show = true;
    285     for (size_t i = 0; i < blockers.size(); ++i) {
    286       if (!blockers[i]->ShouldShowNotification((*iter)->notifier_id())) {
    287         should_show = false;
    288         break;
    289       }
    290     }
    291     if (should_show)
    292       result.insert(*iter);
    293   }
    294 
    295   return result;
    296 }
    297 
    298 size_t NotificationList::NotificationCount(
    299     const NotificationBlockers& blockers) const {
    300   return GetVisibleNotifications(blockers).size();
    301 }
    302 
    303 size_t NotificationList::UnreadCount(
    304     const NotificationBlockers& blockers) const {
    305   Notifications notifications = GetVisibleNotifications(blockers);
    306   size_t unread_count = 0;
    307   for (Notifications::const_iterator iter = notifications.begin();
    308        iter != notifications.end(); ++iter) {
    309     if (!(*iter)->IsRead())
    310       ++unread_count;
    311   }
    312   return unread_count;
    313 }
    314 
    315 NotificationList::Notifications::iterator NotificationList::GetNotification(
    316     const std::string& id) {
    317   for (Notifications::iterator iter = notifications_.begin();
    318        iter != notifications_.end(); ++iter) {
    319     if ((*iter)->id() == id)
    320       return iter;
    321   }
    322   return notifications_.end();
    323 }
    324 
    325 void NotificationList::EraseNotification(Notifications::iterator iter) {
    326   delete *iter;
    327   notifications_.erase(iter);
    328 }
    329 
    330 void NotificationList::PushNotification(scoped_ptr<Notification> notification) {
    331   // Ensure that notification.id is unique by erasing any existing
    332   // notification with the same id (shouldn't normally happen).
    333   Notifications::iterator iter = GetNotification(notification->id());
    334   bool state_inherited = false;
    335   if (iter != notifications_.end()) {
    336     notification->CopyState(*iter);
    337     state_inherited = true;
    338     EraseNotification(iter);
    339   }
    340   // Add the notification to the the list and mark it unread and unshown.
    341   if (!state_inherited) {
    342     // TODO(mukai): needs to distinguish if a notification is dismissed by
    343     // the quiet mode or user operation.
    344     notification->set_is_read(false);
    345     notification->set_shown_as_popup(message_center_visible_
    346                                      || quiet_mode_
    347                                      || notification->shown_as_popup());
    348   }
    349   // Take ownership. The notification can only be removed from the list
    350   // in EraseNotification(), which will delete it.
    351   notifications_.insert(notification.release());
    352 }
    353 
    354 }  // namespace message_center
    355