blob: baf9f017ef953e1169f3187cfa4b51b46f1fccd9 [file] [log] [blame]
// Copyright (c) 2012 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 "ash/system/web_notification/web_notification_tray.h"
#include "ash/shell.h"
#include "ash/system/status_area_widget.h"
#include "ash/system/tray/tray_bubble_view.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/tray/tray_views.h"
#include "ash/system/web_notification/message_center_bubble.h"
#include "ash/system/web_notification/popup_bubble.h"
#include "ash/system/web_notification/web_notification.h"
#include "ash/system/web_notification/web_notification_bubble.h"
#include "ash/system/web_notification/web_notification_list.h"
#include "ash/system/web_notification/web_notification_view.h"
#include "ash/wm/shelf_layout_manager.h"
#include "base/message_loop.h"
#include "base/stringprintf.h"
#include "grit/ash_resources.h"
#include "grit/ash_strings.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/screen.h"
#include "ui/views/widget/widget_observer.h"
namespace {
// Tray constants
const int kTrayContainerVerticalPaddingBottomAlignment = 3;
const int kTrayContainerHorizontalPaddingBottomAlignment = 1;
const int kTrayContainerVerticalPaddingVerticalAlignment = 1;
const int kTrayContainerHorizontalPaddingVerticalAlignment = 0;
const int kPaddingFromLeftEdgeOfSystemTrayBottomAlignment = 8;
const int kPaddingFromTopEdgeOfSystemTrayVerticalAlignment = 10;
std::string GetNotificationText(int notification_count) {
if (notification_count >= 100)
return "99+";
return base::StringPrintf("%d", notification_count);
}
} // namespace
namespace ash {
// WebNotificationTray statics (for unit tests)
// Limit the number of visible notifications.
const size_t WebNotificationTray::kMaxVisibleTrayNotifications = 100;
const size_t WebNotificationTray::kMaxVisiblePopupNotifications = 5;
using message_center::MessageCenterBubble;
using message_center::PopupBubble;
using message_center::WebNotification;
using message_center::WebNotificationBubble;
using message_center::WebNotificationList;
using message_center::WebNotificationView;
WebNotificationTray::WebNotificationTray(
internal::StatusAreaWidget* status_area_widget)
: internal::TrayBackgroundView(status_area_widget),
notification_list_(new WebNotificationList()),
button_(NULL),
delegate_(NULL),
show_message_center_on_unlock_(false) {
button_ = new views::ImageButton(this);
tray_container()->AddChildView(button_);
UpdateTray();
}
WebNotificationTray::~WebNotificationTray() {
// Release any child views that might have back pointers before ~View().
notification_list_.reset();
message_center_bubble_.reset();
popup_bubble_.reset();
}
void WebNotificationTray::SetDelegate(Delegate* delegate) {
DCHECK(!delegate_);
delegate_ = delegate;
}
// Add/Update/RemoveNotification are called by the client code, i.e the
// Delegate implementation or its proxy.
void WebNotificationTray::AddNotification(const std::string& id,
const string16& title,
const string16& message,
const string16& display_source,
const std::string& extension_id) {
notification_list_->AddNotification(
id, title, message, display_source, extension_id);
UpdateTrayAndBubble();
ShowPopupBubble();
}
void WebNotificationTray::UpdateNotification(const std::string& old_id,
const std::string& new_id,
const string16& title,
const string16& message) {
notification_list_->UpdateNotificationMessage(old_id, new_id, title, message);
UpdateTrayAndBubble();
ShowPopupBubble();
}
void WebNotificationTray::RemoveNotification(const std::string& id) {
if (!notification_list_->RemoveNotification(id))
return;
if (!notification_list_->HasPopupNotifications())
HidePopupBubble();
UpdateTrayAndBubble();
}
void WebNotificationTray::SetNotificationImage(const std::string& id,
const gfx::ImageSkia& image) {
if (!notification_list_->SetNotificationImage(id, image))
return;
UpdateTrayAndBubble();
if (popup_bubble())
popup_bubble()->set_dirty(true);
ShowPopupBubble();
}
void WebNotificationTray::ShowMessageCenterBubble() {
if (status_area_widget()->login_status() == user::LOGGED_IN_LOCKED)
return;
if (message_center_bubble()) {
UpdateTray();
return;
}
// Indicate that the message center is visible. Clears the unread count.
notification_list_->SetMessageCenterVisible(true);
UpdateTray();
HidePopupBubble();
message_center_bubble_.reset(new MessageCenterBubble(this));
status_area_widget()->SetHideSystemNotifications(true);
GetShelfLayoutManager()->UpdateAutoHideState();
}
void WebNotificationTray::HideMessageCenterBubble() {
if (!message_center_bubble())
return;
message_center_bubble_.reset();
show_message_center_on_unlock_ = false;
notification_list_->SetMessageCenterVisible(false);
UpdateTray();
status_area_widget()->SetHideSystemNotifications(false);
GetShelfLayoutManager()->UpdateAutoHideState();
}
void WebNotificationTray::SetHidePopupBubble(bool hide) {
if (hide)
HidePopupBubble();
else
ShowPopupBubble();
}
void WebNotificationTray::ShowPopupBubble() {
if (status_area_widget()->login_status() == user::LOGGED_IN_LOCKED)
return;
if (message_center_bubble())
return;
if (!status_area_widget()->ShouldShowWebNotifications())
return;
UpdateTray();
if (popup_bubble()) {
popup_bubble()->ScheduleUpdate();
} else if (notification_list_->HasPopupNotifications()) {
popup_bubble_.reset(new PopupBubble(this));
}
}
void WebNotificationTray::HidePopupBubble() {
popup_bubble_.reset();
}
void WebNotificationTray::UpdateAfterLoginStatusChange(
user::LoginStatus login_status) {
if (login_status == user::LOGGED_IN_LOCKED) {
if (message_center_bubble()) {
message_center_bubble_.reset();
show_message_center_on_unlock_ = true;
}
HidePopupBubble();
} else {
if (show_message_center_on_unlock_)
ShowMessageCenterBubble();
show_message_center_on_unlock_ = false;
}
UpdateTray();
}
bool WebNotificationTray::IsMessageCenterBubbleVisible() const {
return (message_center_bubble() && message_center_bubble_->IsVisible());
}
bool WebNotificationTray::IsMouseInNotificationBubble() const {
if (!popup_bubble())
return false;
return popup_bubble_->bubble_view()->GetBoundsInScreen().Contains(
Shell::GetScreen()->GetCursorScreenPoint());
}
void WebNotificationTray::SetShelfAlignment(ShelfAlignment alignment) {
if (alignment == shelf_alignment())
return;
internal::TrayBackgroundView::SetShelfAlignment(alignment);
// Destroy any existing bubble so that it will be rebuilt correctly.
HideMessageCenterBubble();
HidePopupBubble();
}
void WebNotificationTray::AnchorUpdated() {
if (popup_bubble_.get()) {
popup_bubble_->bubble_view()->UpdateBubble();
// Ensure that the notification buble is above the launcher/status area.
popup_bubble_->bubble_view()->GetWidget()->StackAtTop();
}
if (message_center_bubble_.get())
message_center_bubble_->bubble_view()->UpdateBubble();
}
string16 WebNotificationTray::GetAccessibleName() {
return l10n_util::GetStringUTF16(
IDS_ASH_WEB_NOTIFICATION_TRAY_ACCESSIBLE_NAME);
}
// Private methods invoked by WebNotificationBubble and its child classes
void WebNotificationTray::SendRemoveNotification(const std::string& id) {
// If this is the only notification in the list, close the bubble.
if (notification_list_->notifications().size() == 1 &&
notification_list_->HasNotification(id)) {
HideMessageCenterBubble();
}
if (delegate_)
delegate_->NotificationRemoved(id);
}
void WebNotificationTray::SendRemoveAllNotifications() {
HideMessageCenterBubble();
if (delegate_) {
const WebNotificationList::Notifications& notifications =
notification_list_->notifications();
for (WebNotificationList::Notifications::const_iterator loopiter =
notifications.begin();
loopiter != notifications.end(); ) {
WebNotificationList::Notifications::const_iterator curiter = loopiter++;
std::string notification_id = curiter->id;
// May call RemoveNotification and erase curiter.
delegate_->NotificationRemoved(notification_id);
}
}
}
// When we disable notifications, we remove any existing matching
// notifications to avoid adding complicated UI to re-enable the source.
void WebNotificationTray::DisableByExtension(const std::string& id) {
if (delegate_)
delegate_->DisableExtension(id);
// Will call SendRemoveNotification for each matching notification.
notification_list_->SendRemoveNotificationsByExtension(this, id);
}
void WebNotificationTray::DisableByUrl(const std::string& id) {
if (delegate_)
delegate_->DisableNotificationsFromSource(id);
// Will call SendRemoveNotification for each matching notification.
notification_list_->SendRemoveNotificationsBySource(this, id);
}
bool WebNotificationTray::PerformAction(const ui::Event& event) {
ToggleMessageCenterBubble();
return true;
}
void WebNotificationTray::ButtonPressed(views::Button* sender,
const ui::Event& event) {
DCHECK(sender == button_);
ToggleMessageCenterBubble();
}
void WebNotificationTray::ShowSettings(const std::string& id) {
if (delegate_)
delegate_->ShowSettings(id);
}
void WebNotificationTray::OnClicked(const std::string& id) {
if (delegate_)
delegate_->OnClicked(id);
}
// Other private methods
void WebNotificationTray::ToggleMessageCenterBubble() {
if (message_center_bubble())
HideMessageCenterBubble();
else
ShowMessageCenterBubble();
UpdateTray();
}
void WebNotificationTray::UpdateTray() {
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
if (notification_list()->unread_count() > 0) {
button_->SetImage(views::CustomButton::BS_NORMAL, rb.GetImageSkiaNamed(
IDR_AURA_UBER_TRAY_NOTIFY_BUTTON_ACTIVE_NORMAL));
button_->SetImage(views::CustomButton::BS_HOT, rb.GetImageSkiaNamed(
IDR_AURA_UBER_TRAY_NOTIFY_BUTTON_ACTIVE_HOVER));
button_->SetImage(views::CustomButton::BS_PUSHED, rb.GetImageSkiaNamed(
IDR_AURA_UBER_TRAY_NOTIFY_BUTTON_ACTIVE_PRESSED));
} else {
button_->SetImage(views::CustomButton::BS_NORMAL, rb.GetImageSkiaNamed(
IDR_AURA_UBER_TRAY_NOTIFY_BUTTON_INACTIVE_NORMAL));
button_->SetImage(views::CustomButton::BS_HOT, rb.GetImageSkiaNamed(
IDR_AURA_UBER_TRAY_NOTIFY_BUTTON_INACTIVE_HOVER));
button_->SetImage(views::CustomButton::BS_PUSHED, rb.GetImageSkiaNamed(
IDR_AURA_UBER_TRAY_NOTIFY_BUTTON_INACTIVE_PRESSED));
}
if (message_center_bubble())
button_->SetState(views::CustomButton::BS_PUSHED);
else
button_->SetState(views::CustomButton::BS_NORMAL);
bool is_visible =
(status_area_widget()->login_status() != user::LOGGED_IN_NONE) &&
(status_area_widget()->login_status() != user::LOGGED_IN_LOCKED) &&
(!notification_list()->notifications().empty());
SetVisible(is_visible);
Layout();
SchedulePaint();
}
void WebNotificationTray::UpdateTrayAndBubble() {
if (message_center_bubble()) {
if (notification_list_->notifications().size() == 0)
HideMessageCenterBubble();
else
message_center_bubble()->ScheduleUpdate();
}
if (popup_bubble()) {
if (notification_list_->notifications().size() == 0)
HidePopupBubble();
else
popup_bubble()->ScheduleUpdate();
}
UpdateTray();
}
void WebNotificationTray::HideBubbleWithView(
const TrayBubbleView* bubble_view) {
if (message_center_bubble() &&
bubble_view == message_center_bubble()->bubble_view()) {
HideMessageCenterBubble();
} else if (popup_bubble() && bubble_view == popup_bubble()->bubble_view()) {
HidePopupBubble();
}
}
bool WebNotificationTray::ClickedOutsideBubble() {
// Only hide the message center.
if (!message_center_bubble())
return false;
HideMessageCenterBubble();
return true;
}
// Methods for testing
size_t WebNotificationTray::GetNotificationCountForTest() const {
return notification_list_->notifications().size();
}
bool WebNotificationTray::HasNotificationForTest(const std::string& id) const {
return notification_list_->HasNotification(id);
}
void WebNotificationTray::RemoveAllNotificationsForTest() {
notification_list_->RemoveAllNotifications();
}
size_t WebNotificationTray::GetMessageCenterNotificationCountForTest() const {
if (!message_center_bubble())
return 0;
return message_center_bubble()->NumMessageViewsForTest();
}
size_t WebNotificationTray::GetPopupNotificationCountForTest() const {
if (!popup_bubble())
return 0;
return popup_bubble()->NumMessageViewsForTest();
}
} // namespace ash