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.
    104   if ((*iter)->priority() < new_notification->priority()) {
    105     new_notification->set_is_read(false);
    106     new_notification->set_shown_as_popup(false);
    107   }
    108 
    109   // Do not use EraseNotification and PushNotification, since we don't want to
    110   // change unread counts nor to update is_read/shown_as_popup states.
    111   Notification* old = *iter;
    112   notifications_.erase(iter);
    113   delete old;
    114 
    115   // We really don't want duplicate IDs.
    116   DCHECK(GetNotification(new_notification->id()) == notifications_.end());
    117   notifications_.insert(new_notification.release());
    118 }
    119 
    120 void NotificationList::RemoveNotification(const std::string& id) {
    121   EraseNotification(GetNotification(id));
    122 }
    123 
    124 NotificationList::Notifications NotificationList::GetNotificationsByNotifierId(
    125         const NotifierId& notifier_id) {
    126   Notifications notifications;
    127   for (Notifications::iterator iter = notifications_.begin();
    128        iter != notifications_.end(); ++iter) {
    129     if ((*iter)->notifier_id() == notifier_id)
    130       notifications.insert(*iter);
    131   }
    132   return notifications;
    133 }
    134 
    135 bool NotificationList::SetNotificationIcon(const std::string& notification_id,
    136                                            const gfx::Image& image) {
    137   Notifications::iterator iter = GetNotification(notification_id);
    138   if (iter == notifications_.end())
    139     return false;
    140   (*iter)->set_icon(image);
    141   return true;
    142 }
    143 
    144 bool NotificationList::SetNotificationImage(const std::string& notification_id,
    145                                             const gfx::Image& image) {
    146   Notifications::iterator iter = GetNotification(notification_id);
    147   if (iter == notifications_.end())
    148     return false;
    149   (*iter)->set_image(image);
    150   return true;
    151 }
    152 
    153 bool NotificationList::SetNotificationButtonIcon(
    154     const std::string& notification_id, int button_index,
    155     const gfx::Image& image) {
    156   Notifications::iterator iter = GetNotification(notification_id);
    157   if (iter == notifications_.end())
    158     return false;
    159   (*iter)->SetButtonIcon(button_index, image);
    160   return true;
    161 }
    162 
    163 bool NotificationList::HasNotificationOfType(const std::string& id,
    164                                              const NotificationType type) {
    165   Notifications::iterator iter = GetNotification(id);
    166   if (iter == notifications_.end())
    167     return false;
    168 
    169   return (*iter)->type() == type;
    170 }
    171 
    172 bool NotificationList::HasPopupNotifications(
    173     const NotificationBlockers& blockers) {
    174   for (Notifications::iterator iter = notifications_.begin();
    175        iter != notifications_.end(); ++iter) {
    176     if ((*iter)->priority() < DEFAULT_PRIORITY)
    177       break;
    178     if (!ShouldShowNotificationAsPopup(**iter, blockers))
    179       continue;
    180     if (!(*iter)->shown_as_popup())
    181       return true;
    182   }
    183   return false;
    184 }
    185 
    186 NotificationList::PopupNotifications NotificationList::GetPopupNotifications(
    187     const NotificationBlockers& blockers,
    188     std::list<std::string>* blocked_ids) {
    189   PopupNotifications result;
    190   size_t default_priority_popup_count = 0;
    191 
    192   // Collect notifications that should be shown as popups. Start from oldest.
    193   for (Notifications::const_reverse_iterator iter = notifications_.rbegin();
    194        iter != notifications_.rend(); iter++) {
    195     if ((*iter)->shown_as_popup())
    196       continue;
    197 
    198     // No popups for LOW/MIN priority.
    199     if ((*iter)->priority() < DEFAULT_PRIORITY)
    200       continue;
    201 
    202     if (!ShouldShowNotificationAsPopup(**iter, blockers)) {
    203       if (blocked_ids)
    204         blocked_ids->push_back((*iter)->id());
    205       continue;
    206     }
    207 
    208     // Checking limits. No limits for HIGH/MAX priority. DEFAULT priority
    209     // will return at most kMaxVisiblePopupNotifications entries. If the
    210     // popup entries are more, older entries are used. see crbug.com/165768
    211     if ((*iter)->priority() == DEFAULT_PRIORITY &&
    212         default_priority_popup_count++ >= kMaxVisiblePopupNotifications) {
    213       continue;
    214     }
    215 
    216     result.insert(*iter);
    217   }
    218   return result;
    219 }
    220 
    221 void NotificationList::MarkSinglePopupAsShown(
    222     const std::string& id, bool mark_notification_as_read) {
    223   Notifications::iterator iter = GetNotification(id);
    224   DCHECK(iter != notifications_.end());
    225 
    226   if ((*iter)->shown_as_popup())
    227     return;
    228 
    229   // System notification is marked as shown only when marked as read.
    230   if ((*iter)->priority() != SYSTEM_PRIORITY || mark_notification_as_read)
    231     (*iter)->set_shown_as_popup(true);
    232 
    233   // The popup notification is already marked as read when it's displayed.
    234   // Set the is_read() back to false if necessary.
    235   if (!mark_notification_as_read)
    236     (*iter)->set_is_read(false);
    237 }
    238 
    239 void NotificationList::MarkSinglePopupAsDisplayed(const std::string& id) {
    240   Notifications::iterator iter = GetNotification(id);
    241   if (iter == notifications_.end())
    242     return;
    243 
    244   if ((*iter)->shown_as_popup())
    245     return;
    246 
    247   if (!(*iter)->IsRead())
    248     (*iter)->set_is_read(true);
    249 }
    250 
    251 NotificationDelegate* NotificationList::GetNotificationDelegate(
    252     const std::string& id) {
    253   Notifications::iterator iter = GetNotification(id);
    254   if (iter == notifications_.end())
    255     return NULL;
    256   return (*iter)->delegate();
    257 }
    258 
    259 void NotificationList::SetQuietMode(bool quiet_mode) {
    260   quiet_mode_ = quiet_mode;
    261   if (quiet_mode_) {
    262     for (Notifications::iterator iter = notifications_.begin();
    263          iter != notifications_.end();
    264          ++iter) {
    265       (*iter)->set_shown_as_popup(true);
    266     }
    267   }
    268 }
    269 
    270 Notification* NotificationList::GetNotificationById(const std::string& id) {
    271   Notifications::iterator iter = GetNotification(id);
    272   if (iter != notifications_.end())
    273     return *iter;
    274   return NULL;
    275 }
    276 
    277 NotificationList::Notifications NotificationList::GetVisibleNotifications(
    278     const NotificationBlockers& blockers) const {
    279   Notifications result;
    280   for (Notifications::const_iterator iter = notifications_.begin();
    281        iter != notifications_.end(); ++iter) {
    282     bool should_show = true;
    283     for (size_t i = 0; i < blockers.size(); ++i) {
    284       if (!blockers[i]->ShouldShowNotification((*iter)->notifier_id())) {
    285         should_show = false;
    286         break;
    287       }
    288     }
    289     if (should_show)
    290       result.insert(*iter);
    291   }
    292 
    293   return result;
    294 }
    295 
    296 size_t NotificationList::NotificationCount(
    297     const NotificationBlockers& blockers) const {
    298   return GetVisibleNotifications(blockers).size();
    299 }
    300 
    301 size_t NotificationList::UnreadCount(
    302     const NotificationBlockers& blockers) const {
    303   Notifications notifications = GetVisibleNotifications(blockers);
    304   size_t unread_count = 0;
    305   for (Notifications::const_iterator iter = notifications.begin();
    306        iter != notifications.end(); ++iter) {
    307     if (!(*iter)->IsRead())
    308       ++unread_count;
    309   }
    310   return unread_count;
    311 }
    312 
    313 NotificationList::Notifications::iterator NotificationList::GetNotification(
    314     const std::string& id) {
    315   for (Notifications::iterator iter = notifications_.begin();
    316        iter != notifications_.end(); ++iter) {
    317     if ((*iter)->id() == id)
    318       return iter;
    319   }
    320   return notifications_.end();
    321 }
    322 
    323 void NotificationList::EraseNotification(Notifications::iterator iter) {
    324   delete *iter;
    325   notifications_.erase(iter);
    326 }
    327 
    328 void NotificationList::PushNotification(scoped_ptr<Notification> notification) {
    329   // Ensure that notification.id is unique by erasing any existing
    330   // notification with the same id (shouldn't normally happen).
    331   Notifications::iterator iter = GetNotification(notification->id());
    332   bool state_inherited = false;
    333   if (iter != notifications_.end()) {
    334     notification->CopyState(*iter);
    335     state_inherited = true;
    336     EraseNotification(iter);
    337   }
    338   // Add the notification to the the list and mark it unread and unshown.
    339   if (!state_inherited) {
    340     // TODO(mukai): needs to distinguish if a notification is dismissed by
    341     // the quiet mode or user operation.
    342     notification->set_is_read(false);
    343     notification->set_shown_as_popup(message_center_visible_
    344                                      || quiet_mode_
    345                                      || notification->shown_as_popup());
    346   }
    347   // Take ownership. The notification can only be removed from the list
    348   // in EraseNotification(), which will delete it.
    349   notifications_.insert(notification.release());
    350 }
    351 
    352 }  // namespace message_center
    353