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