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/message_center/message_center_style.h"
     13 #include "ui/message_center/notification.h"
     14 #include "ui/message_center/notification_types.h"
     15 
     16 namespace message_center {
     17 
     18 namespace {
     19 
     20 bool ShouldShowNotificationAsPopup(
     21     const Notification& notification,
     22     const NotificationBlockers& blockers) {
     23   for (size_t i = 0; i < blockers.size(); ++i) {
     24     if (!blockers[i]->ShouldShowNotificationAsPopup(notification.notifier_id()))
     25       return false;
     26   }
     27   return true;
     28 }
     29 
     30 }  // namespace
     31 
     32 bool ComparePriorityTimestampSerial::operator()(Notification* n1,
     33                                                 Notification* n2) {
     34   if (n1->priority() > n2->priority())  // Higher pri go first.
     35     return true;
     36   if (n1->priority() < n2->priority())
     37     return false;
     38   return CompareTimestampSerial()(n1, n2);
     39 }
     40 
     41 bool CompareTimestampSerial::operator()(Notification* n1, Notification* n2) {
     42   if (n1->timestamp() > n2->timestamp())  // Newer come first.
     43     return true;
     44   if (n1->timestamp() < n2->timestamp())
     45     return false;
     46   if (n1->serial_number() > n2->serial_number())  // Newer come first.
     47     return true;
     48   if (n1->serial_number() < n2->serial_number())
     49     return false;
     50   return false;
     51 }
     52 
     53 NotificationList::NotificationList()
     54     : message_center_visible_(false),
     55       quiet_mode_(false) {
     56 }
     57 
     58 NotificationList::~NotificationList() {
     59   STLDeleteContainerPointers(notifications_.begin(), notifications_.end());
     60 }
     61 
     62 void NotificationList::SetMessageCenterVisible(
     63     bool visible,
     64     std::set<std::string>* updated_ids) {
     65   if (message_center_visible_ == visible)
     66     return;
     67 
     68   message_center_visible_ = visible;
     69 
     70   if (!visible)
     71     return;
     72 
     73   for (Notifications::iterator iter = notifications_.begin();
     74        iter != notifications_.end(); ++iter) {
     75     Notification* notification = *iter;
     76     bool was_popup = notification->shown_as_popup();
     77     bool was_read = notification->IsRead();
     78     if (notification->priority() < SYSTEM_PRIORITY)
     79       notification->set_shown_as_popup(true);
     80     notification->set_is_read(true);
     81     if (updated_ids && !(was_popup && was_read))
     82       updated_ids->insert(notification->id());
     83   }
     84 }
     85 
     86 void NotificationList::AddNotification(scoped_ptr<Notification> notification) {
     87   PushNotification(notification.Pass());
     88 }
     89 
     90 void NotificationList::UpdateNotificationMessage(
     91     const std::string& old_id,
     92     scoped_ptr<Notification> new_notification) {
     93   Notifications::iterator iter = GetNotification(old_id);
     94   if (iter == notifications_.end())
     95     return;
     96 
     97   new_notification->CopyState(*iter);
     98 
     99   // Handles priority promotion. If the notification is already dismissed but
    100   // the updated notification has higher priority, it should re-appear as a
    101   // toast.
    102   if ((*iter)->priority() < new_notification->priority()) {
    103     new_notification->set_is_read(false);
    104     new_notification->set_shown_as_popup(false);
    105   }
    106 
    107   // Do not use EraseNotification and PushNotification, since we don't want to
    108   // change unread counts nor to update is_read/shown_as_popup states.
    109   Notification* old = *iter;
    110   notifications_.erase(iter);
    111   delete old;
    112 
    113   // We really don't want duplicate IDs.
    114   DCHECK(GetNotification(new_notification->id()) == notifications_.end());
    115   notifications_.insert(new_notification.release());
    116 }
    117 
    118 void NotificationList::RemoveNotification(const std::string& id) {
    119   EraseNotification(GetNotification(id));
    120 }
    121 
    122 NotificationList::Notifications NotificationList::GetNotificationsByNotifierId(
    123         const NotifierId& notifier_id) {
    124   Notifications notifications;
    125   for (Notifications::iterator iter = notifications_.begin();
    126        iter != notifications_.end(); ++iter) {
    127     if ((*iter)->notifier_id() == notifier_id)
    128       notifications.insert(*iter);
    129   }
    130   return notifications;
    131 }
    132 
    133 bool NotificationList::SetNotificationIcon(const std::string& notification_id,
    134                                            const gfx::Image& image) {
    135   Notifications::iterator iter = GetNotification(notification_id);
    136   if (iter == notifications_.end())
    137     return false;
    138   (*iter)->set_icon(image);
    139   return true;
    140 }
    141 
    142 bool NotificationList::SetNotificationImage(const std::string& notification_id,
    143                                             const gfx::Image& image) {
    144   Notifications::iterator iter = GetNotification(notification_id);
    145   if (iter == notifications_.end())
    146     return false;
    147   (*iter)->set_image(image);
    148   return true;
    149 }
    150 
    151 bool NotificationList::SetNotificationButtonIcon(
    152     const std::string& notification_id, int button_index,
    153     const gfx::Image& image) {
    154   Notifications::iterator iter = GetNotification(notification_id);
    155   if (iter == notifications_.end())
    156     return false;
    157   (*iter)->SetButtonIcon(button_index, image);
    158   return true;
    159 }
    160 
    161 bool NotificationList::HasNotification(const std::string& id) {
    162   return GetNotification(id) != notifications_.end();
    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 void NotificationList::MarkNotificationAsExpanded(const std::string& id) {
    254   Notifications::iterator iter = GetNotification(id);
    255   if (iter != notifications_.end())
    256     (*iter)->set_is_expanded(true);
    257 }
    258 
    259 NotificationDelegate* NotificationList::GetNotificationDelegate(
    260     const std::string& id) {
    261   Notifications::iterator iter = GetNotification(id);
    262   if (iter == notifications_.end())
    263     return NULL;
    264   return (*iter)->delegate();
    265 }
    266 
    267 void NotificationList::SetQuietMode(bool quiet_mode) {
    268   quiet_mode_ = quiet_mode;
    269   if (quiet_mode_) {
    270     for (Notifications::iterator iter = notifications_.begin();
    271          iter != notifications_.end();
    272          ++iter) {
    273       (*iter)->set_shown_as_popup(true);
    274     }
    275   }
    276 }
    277 
    278 NotificationList::Notifications NotificationList::GetVisibleNotifications(
    279     const NotificationBlockers& blockers) const {
    280   Notifications result;
    281   for (Notifications::const_iterator iter = notifications_.begin();
    282        iter != notifications_.end(); ++iter) {
    283     bool should_show = true;
    284     for (size_t i = 0; i < blockers.size(); ++i) {
    285       if (!blockers[i]->ShouldShowNotification((*iter)->notifier_id())) {
    286         should_show = false;
    287         break;
    288       }
    289     }
    290     if (should_show)
    291       result.insert(*iter);
    292   }
    293 
    294   return result;
    295 }
    296 
    297 size_t NotificationList::NotificationCount(
    298     const NotificationBlockers& blockers) const {
    299   return GetVisibleNotifications(blockers).size();
    300 }
    301 
    302 size_t NotificationList::UnreadCount(
    303     const NotificationBlockers& blockers) const {
    304   Notifications notifications = GetVisibleNotifications(blockers);
    305   size_t unread_count = 0;
    306   for (Notifications::const_iterator iter = notifications.begin();
    307        iter != notifications.end(); ++iter) {
    308     if (!(*iter)->IsRead())
    309       ++unread_count;
    310   }
    311   return unread_count;
    312 }
    313 
    314 NotificationList::Notifications::iterator NotificationList::GetNotification(
    315     const std::string& id) {
    316   for (Notifications::iterator iter = notifications_.begin();
    317        iter != notifications_.end(); ++iter) {
    318     if ((*iter)->id() == id)
    319       return iter;
    320   }
    321   return notifications_.end();
    322 }
    323 
    324 void NotificationList::EraseNotification(Notifications::iterator iter) {
    325   delete *iter;
    326   notifications_.erase(iter);
    327 }
    328 
    329 void NotificationList::PushNotification(scoped_ptr<Notification> notification) {
    330   // Ensure that notification.id is unique by erasing any existing
    331   // notification with the same id (shouldn't normally happen).
    332   Notifications::iterator iter = GetNotification(notification->id());
    333   bool state_inherited = false;
    334   if (iter != notifications_.end()) {
    335     notification->CopyState(*iter);
    336     state_inherited = true;
    337     EraseNotification(iter);
    338   }
    339   // Add the notification to the the list and mark it unread and unshown.
    340   if (!state_inherited) {
    341     // TODO(mukai): needs to distinguish if a notification is dismissed by
    342     // the quiet mode or user operation.
    343     notification->set_is_read(false);
    344     notification->set_shown_as_popup(message_center_visible_
    345                                      || quiet_mode_
    346                                      || notification->shown_as_popup());
    347   }
    348   // Take ownership. The notification can only be removed from the list
    349   // in EraseNotification(), which will delete it.
    350   notifications_.insert(notification.release());
    351 }
    352 
    353 }  // namespace message_center
    354