blob: c1d0179dc00c49eb5727b3ac50c4508808bb3c86 [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/message_center/message_center_impl.h"
#include "base/observer_list.h"
#include "ui/message_center/message_center_style.h"
#include "ui/message_center/notification.h"
#include "ui/message_center/notification_list.h"
#include "ui/message_center/notification_types.h"
namespace {
base::TimeDelta GetTimeoutForPriority(int priority) {
if (priority > message_center::DEFAULT_PRIORITY) {
return base::TimeDelta::FromSeconds(
message_center::kAutocloseHighPriorityDelaySeconds);
}
return base::TimeDelta::FromSeconds(
message_center::kAutocloseDefaultDelaySeconds);
}
} // namespace
namespace message_center {
namespace internal {
////////////////////////////////////////////////////////////////////////////////
// PopupTimer
PopupTimer::PopupTimer(const std::string& id,
base::TimeDelta timeout,
base::WeakPtr<PopupTimersController> controller)
: id_(id),
timeout_(timeout),
timer_controller_(controller),
timer_(new base::OneShotTimer<PopupTimersController>) {}
PopupTimer::~PopupTimer() {
if (!timer_)
return;
if (timer_->IsRunning())
timer_->Stop();
}
void PopupTimer::Start() {
if (timer_->IsRunning())
return;
base::TimeDelta timeout_to_close =
timeout_ <= passed_ ? base::TimeDelta() : timeout_ - passed_;
start_time_ = base::Time::Now();
timer_->Start(
FROM_HERE,
timeout_to_close,
base::Bind(
&PopupTimersController::TimerFinished, timer_controller_, id_));
}
void PopupTimer::Pause() {
if (!timer_.get() || !timer_->IsRunning())
return;
timer_->Stop();
passed_ += base::Time::Now() - start_time_;
}
void PopupTimer::Reset() {
if (timer_)
timer_->Stop();
passed_ = base::TimeDelta();
}
////////////////////////////////////////////////////////////////////////////////
// PopupTimersController
PopupTimersController::PopupTimersController(MessageCenter* message_center)
: message_center_(message_center), popup_deleter_(&popup_timers_) {
message_center_->AddObserver(this);
}
PopupTimersController::~PopupTimersController() {
message_center_->RemoveObserver(this);
}
void PopupTimersController::StartTimer(const std::string& id,
const base::TimeDelta& timeout) {
PopupTimerCollection::iterator iter = popup_timers_.find(id);
if (iter != popup_timers_.end()) {
DCHECK(iter->second);
iter->second->Start();
return;
}
PopupTimer* timer = new PopupTimer(id, timeout, AsWeakPtr());
timer->Start();
popup_timers_[id] = timer;
}
void PopupTimersController::StartAll() {
std::map<std::string, PopupTimer*>::iterator iter;
for (iter = popup_timers_.begin(); iter != popup_timers_.end(); iter++) {
iter->second->Start();
}
}
void PopupTimersController::ResetTimer(const std::string& id,
const base::TimeDelta& timeout) {
CancelTimer(id);
StartTimer(id, timeout);
}
void PopupTimersController::PauseTimer(const std::string& id) {
PopupTimerCollection::iterator iter = popup_timers_.find(id);
if (iter == popup_timers_.end())
return;
iter->second->Pause();
}
void PopupTimersController::PauseAll() {
std::map<std::string, PopupTimer*>::iterator iter;
for (iter = popup_timers_.begin(); iter != popup_timers_.end(); iter++) {
iter->second->Pause();
}
}
void PopupTimersController::CancelTimer(const std::string& id) {
PopupTimerCollection::iterator iter = popup_timers_.find(id);
if (iter == popup_timers_.end())
return;
PopupTimer* timer = iter->second;
delete timer;
popup_timers_.erase(iter);
}
void PopupTimersController::CancelAll() {
STLDeleteValues(&popup_timers_);
popup_timers_.clear();
}
void PopupTimersController::TimerFinished(const std::string& id) {
PopupTimerCollection::iterator iter = popup_timers_.find(id);
if (iter == popup_timers_.end())
return;
CancelTimer(id);
message_center_->MarkSinglePopupAsShown(id, false);
}
void PopupTimersController::OnNotificationDisplayed(const std::string& id) {
OnNotificationUpdated(id);
}
void PopupTimersController::OnNotificationUpdated(const std::string& id) {
NotificationList::PopupNotifications popup_notifications =
message_center_->GetPopupNotifications();
if (!popup_notifications.size()) {
CancelAll();
return;
}
NotificationList::PopupNotifications::const_iterator iter =
popup_notifications.begin();
for (; iter != popup_notifications.end(); iter++) {
if ((*iter)->id() == id)
break;
}
if (iter == popup_notifications.end() || (*iter)->never_timeout()) {
CancelTimer(id);
return;
}
// Start the timer if not yet.
if (popup_timers_.find(id) == popup_timers_.end())
StartTimer(id, GetTimeoutForPriority((*iter)->priority()));
}
void PopupTimersController::OnNotificationRemoved(const std::string& id,
bool by_user) {
CancelTimer(id);
}
} // namespace internal
////////////////////////////////////////////////////////////////////////////////
// MessageCenterImpl
MessageCenterImpl::MessageCenterImpl()
: MessageCenter(),
popup_timers_controller_(new internal::PopupTimersController(this)),
delegate_(NULL),
settings_provider_(NULL) {
notification_list_.reset(new NotificationList());
}
MessageCenterImpl::~MessageCenterImpl() {
notification_list_.reset();
}
void MessageCenterImpl::AddObserver(MessageCenterObserver* observer) {
observer_list_.AddObserver(observer);
}
void MessageCenterImpl::RemoveObserver(MessageCenterObserver* observer) {
observer_list_.RemoveObserver(observer);
}
void MessageCenterImpl::SetDelegate(Delegate* delegate) {
delegate_ = delegate;
}
void MessageCenterImpl::SetMessageCenterVisible(bool visible) {
std::set<std::string> updated_ids;
notification_list_->SetMessageCenterVisible(visible, &updated_ids);
for (std::set<std::string>::const_iterator iter = updated_ids.begin();
iter != updated_ids.end();
++iter) {
FOR_EACH_OBSERVER(
MessageCenterObserver, observer_list_, OnNotificationUpdated(*iter));
}
if (!visible) {
FOR_EACH_OBSERVER(
MessageCenterObserver, observer_list_, OnNotificationCenterClosed());
}
}
bool MessageCenterImpl::IsMessageCenterVisible() {
return notification_list_->is_message_center_visible();
}
size_t MessageCenterImpl::NotificationCount() const {
return notification_list_->NotificationCount();
}
size_t MessageCenterImpl::UnreadNotificationCount() const {
return notification_list_->unread_count();
}
bool MessageCenterImpl::HasPopupNotifications() const {
return notification_list_->HasPopupNotifications();
}
bool MessageCenterImpl::HasNotification(const std::string& id) {
return notification_list_->HasNotification(id);
}
bool MessageCenterImpl::IsQuietMode() const {
return notification_list_->quiet_mode();
}
bool MessageCenterImpl::HasClickedListener(const std::string& id) {
NotificationDelegate* delegate =
notification_list_->GetNotificationDelegate(id);
return delegate && delegate->HasClickedListener();
}
const NotificationList::Notifications& MessageCenterImpl::GetNotifications() {
return notification_list_->GetNotifications();
}
NotificationList::PopupNotifications
MessageCenterImpl::GetPopupNotifications() {
return notification_list_->GetPopupNotifications();
}
//------------------------------------------------------------------------------
// Client code interface.
void MessageCenterImpl::AddNotification(scoped_ptr<Notification> notification) {
DCHECK(notification.get());
// Sometimes the notification can be added with the same id and the
// |notification_list| will replace the notification instead of adding new.
// This is essentially an update rather than addition.
const std::string& id = notification->id();
bool already_exists = notification_list_->HasNotification(id);
notification_list_->AddNotification(notification.Pass());
if (already_exists) {
FOR_EACH_OBSERVER(
MessageCenterObserver, observer_list_, OnNotificationUpdated(id));
} else {
FOR_EACH_OBSERVER(
MessageCenterObserver, observer_list_, OnNotificationAdded(id));
}
}
void MessageCenterImpl::UpdateNotification(
const std::string& old_id,
scoped_ptr<Notification> new_notification) {
std::string new_id = new_notification->id();
notification_list_->UpdateNotificationMessage(old_id,
new_notification.Pass());
if (old_id == new_id) {
FOR_EACH_OBSERVER(
MessageCenterObserver, observer_list_, OnNotificationUpdated(new_id));
} else {
FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_,
OnNotificationRemoved(old_id, false));
FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_,
OnNotificationAdded(new_id));
}
}
void MessageCenterImpl::RemoveNotification(const std::string& id,
bool by_user) {
if (!HasNotification(id))
return;
NotificationDelegate* delegate =
notification_list_->GetNotificationDelegate(id);
if (delegate)
delegate->Close(by_user);
// In many cases |id| is a reference to an existing notification instance
// but the instance can be destructed in RemoveNotification(). Hence
// copies the id explicitly here.
std::string copied_id(id);
notification_list_->RemoveNotification(copied_id);
FOR_EACH_OBSERVER(MessageCenterObserver,
observer_list_,
OnNotificationRemoved(copied_id, by_user));
}
void MessageCenterImpl::RemoveAllNotifications(bool by_user) {
const NotificationList::Notifications& notifications =
notification_list_->GetNotifications();
std::set<std::string> ids;
for (NotificationList::Notifications::const_iterator iter =
notifications.begin(); iter != notifications.end(); ++iter) {
ids.insert((*iter)->id());
}
notification_list_->RemoveAllNotifications();
for (std::set<std::string>::const_iterator iter = ids.begin();
iter != ids.end(); ++iter) {
FOR_EACH_OBSERVER(MessageCenterObserver,
observer_list_,
OnNotificationRemoved(*iter, by_user));
}
}
void MessageCenterImpl::SetNotificationIcon(const std::string& notification_id,
const gfx::Image& image) {
if (notification_list_->SetNotificationIcon(notification_id, image)) {
FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_,
OnNotificationUpdated(notification_id));
}
}
void MessageCenterImpl::SetNotificationImage(const std::string& notification_id,
const gfx::Image& image) {
if (notification_list_->SetNotificationImage(notification_id, image)) {
FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_,
OnNotificationUpdated(notification_id));
}
}
void MessageCenterImpl::SetNotificationButtonIcon(
const std::string& notification_id, int button_index,
const gfx::Image& image) {
if (!HasNotification(notification_id))
return;
if (notification_list_->SetNotificationButtonIcon(notification_id,
button_index, image)) {
FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_,
OnNotificationUpdated(notification_id));
}
}
void MessageCenterImpl::DisableNotificationsByExtension(
const std::string& id) {
if (delegate_)
delegate_->DisableExtension(id);
NotificationList::Notifications notifications =
notification_list_->GetNotificationsByExtension(id);
for (NotificationList::Notifications::const_iterator iter =
notifications.begin(); iter != notifications.end();) {
std::string id = (*iter)->id();
iter++;
RemoveNotification(id, false);
}
}
void MessageCenterImpl::DisableNotificationsByUrl(const std::string& id) {
if (delegate_)
delegate_->DisableNotificationsFromSource(id);
NotificationList::Notifications notifications =
notification_list_->GetNotificationsBySource(id);
for (NotificationList::Notifications::const_iterator iter =
notifications.begin(); iter != notifications.end();) {
std::string id = (*iter)->id();
iter++;
RemoveNotification(id, false);
}
}
void MessageCenterImpl::ShowNotificationSettings(const std::string& id) {
if (delegate_)
delegate_->ShowSettings(id);
}
void MessageCenterImpl::ExpandNotification(const std::string& id) {
if (!HasNotification(id))
return;
notification_list_->MarkNotificationAsExpanded(id);
FOR_EACH_OBSERVER(MessageCenterObserver, observer_list_,
OnNotificationUpdated(id));
}
void MessageCenterImpl::ClickOnNotification(const std::string& id) {
if (!HasNotification(id))
return;
if (HasPopupNotifications())
MarkSinglePopupAsShown(id, true);
NotificationDelegate* delegate =
notification_list_->GetNotificationDelegate(id);
if (delegate)
delegate->Click();
FOR_EACH_OBSERVER(
MessageCenterObserver, observer_list_, OnNotificationClicked(id));
}
void MessageCenterImpl::ClickOnNotificationButton(const std::string& id,
int button_index) {
if (!HasNotification(id))
return;
if (HasPopupNotifications())
MarkSinglePopupAsShown(id, true);
NotificationDelegate* delegate =
notification_list_->GetNotificationDelegate(id);
if (delegate)
delegate->ButtonClick(button_index);
FOR_EACH_OBSERVER(
MessageCenterObserver, observer_list_, OnNotificationButtonClicked(
id, button_index));
}
void MessageCenterImpl::MarkSinglePopupAsShown(const std::string& id,
bool mark_notification_as_read) {
if (!HasNotification(id))
return;
notification_list_->MarkSinglePopupAsShown(id, mark_notification_as_read);
FOR_EACH_OBSERVER(
MessageCenterObserver, observer_list_, OnNotificationUpdated(id));
}
void MessageCenterImpl::DisplayedNotification(const std::string& id) {
if (!HasNotification(id))
return;
if (HasPopupNotifications())
notification_list_->MarkSinglePopupAsDisplayed(id);
NotificationDelegate* delegate =
notification_list_->GetNotificationDelegate(id);
if (delegate)
delegate->Display();
FOR_EACH_OBSERVER(
MessageCenterObserver, observer_list_, OnNotificationDisplayed(id));
}
void MessageCenterImpl::SetNotifierSettingsProvider(
NotifierSettingsProvider* provider) {
settings_provider_ = provider;
}
NotifierSettingsProvider* MessageCenterImpl::GetNotifierSettingsProvider() {
return settings_provider_;
}
void MessageCenterImpl::SetQuietMode(bool in_quiet_mode) {
notification_list_->SetQuietMode(in_quiet_mode);
}
void MessageCenterImpl::EnterQuietModeWithExpire(
const base::TimeDelta& expires_in) {
notification_list_->EnterQuietModeWithExpire(expires_in);
}
void MessageCenterImpl::RestartPopupTimers() {
if (popup_timers_controller_.get())
popup_timers_controller_->StartAll();
}
void MessageCenterImpl::PausePopupTimers() {
if (popup_timers_controller_.get())
popup_timers_controller_->PauseAll();
}
void MessageCenterImpl::DisableTimersForTest() {
popup_timers_controller_.reset();
}
} // namespace message_center