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.
5 #include "ash/system/web_notification/web_notification_tray.h"
8 #include "ash/shell_window_ids.h"
9 #include "ash/system/status_area_widget.h"
10 #include "ash/system/tray/tray_bubble_wrapper.h"
11 #include "ash/system/tray/tray_constants.h"
12 #include "ash/system/tray/tray_views.h"
13 #include "ash/wm/shelf_layout_manager.h"
14 #include "base/message_loop.h"
15 #include "base/stringprintf.h"
16 #include "grit/ash_resources.h"
17 #include "grit/ash_strings.h"
18 #include "ui/aura/window.h"
19 #include "ui/base/l10n/l10n_util.h"
20 #include "ui/base/resource/resource_bundle.h"
21 #include "ui/gfx/screen.h"
22 #include "ui/message_center/message_center_bubble.h"
23 #include "ui/message_center/message_popup_bubble.h"
24 #include "ui/message_center/quiet_mode_bubble.h"
25 #include "ui/views/bubble/tray_bubble_view.h"
26 #include "ui/views/widget/widget_observer.h"
31 const int kTrayContainerVerticalPaddingBottomAlignment
= 3;
32 const int kTrayContainerHorizontalPaddingBottomAlignment
= 1;
33 const int kTrayContainerVerticalPaddingVerticalAlignment
= 1;
34 const int kTrayContainerHorizontalPaddingVerticalAlignment
= 0;
35 const int kPaddingFromLeftEdgeOfSystemTrayBottomAlignment
= 8;
36 const int kPaddingFromTopEdgeOfSystemTrayVerticalAlignment
= 10;
44 // Class to initialize and manage the WebNotificationBubble and
45 // TrayBubbleWrapper instances for a bubble.
47 class WebNotificationBubbleWrapper
{
49 // Takes ownership of |bubble| and creates |bubble_wrapper_|.
50 WebNotificationBubbleWrapper(WebNotificationTray
* tray
,
51 message_center::MessageBubbleBase
* bubble
) {
52 bubble_
.reset(bubble
);
53 views::TrayBubbleView::AnchorAlignment anchor_alignment
=
54 tray
->GetAnchorAlignment();
55 views::TrayBubbleView::InitParams init_params
=
56 bubble
->GetInitParams(anchor_alignment
);
57 views::View
* anchor
= tray
->tray_container();
58 if (anchor_alignment
== views::TrayBubbleView::ANCHOR_ALIGNMENT_BOTTOM
) {
59 gfx::Point
bounds(anchor
->width() / 2, 0);
60 views::View::ConvertPointToWidget(anchor
, &bounds
);
61 init_params
.arrow_offset
= bounds
.x();
63 views::TrayBubbleView
* bubble_view
= views::TrayBubbleView::Create(
64 tray
->GetBubbleWindowContainer(), anchor
, tray
, &init_params
);
65 bubble_wrapper_
.reset(new TrayBubbleWrapper(tray
, bubble_view
));
66 bubble
->InitializeContents(bubble_view
);
69 message_center::MessageBubbleBase
* bubble() const { return bubble_
.get(); }
71 // Convenience accessors.
72 views::TrayBubbleView
* bubble_view() const { return bubble_
->bubble_view(); }
75 scoped_ptr
<message_center::MessageBubbleBase
> bubble_
;
76 scoped_ptr
<internal::TrayBubbleWrapper
> bubble_wrapper_
;
79 } // namespace internal
81 WebNotificationTray::WebNotificationTray(
82 internal::StatusAreaWidget
* status_area_widget
)
83 : internal::TrayBackgroundView(status_area_widget
),
85 show_message_center_on_unlock_(false) {
86 message_center_
.reset(new message_center::MessageCenter(this));
87 button_
= new views::ImageButton(this);
88 button_
->set_triggerable_event_flags(
89 ui::EF_LEFT_MOUSE_BUTTON
| ui::EF_RIGHT_MOUSE_BUTTON
);
90 tray_container()->AddChildView(button_
);
94 WebNotificationTray::~WebNotificationTray() {
95 // message_center_ has a weak pointer to this; destroy it early.
96 message_center_
.reset();
97 // Release any child views that might have back pointers before ~View().
98 message_center_bubble_
.reset();
99 popup_bubble_
.reset();
100 if (quiet_mode_bubble() && quiet_mode_bubble_
->GetBubbleWidget())
101 quiet_mode_bubble_
->GetBubbleWidget()->RemoveObserver(this);
102 quiet_mode_bubble_
.reset();
105 void WebNotificationTray::ShowMessageCenterBubble() {
106 if (status_area_widget()->login_status() == user::LOGGED_IN_LOCKED
)
108 if (quiet_mode_bubble())
109 quiet_mode_bubble_
.reset();
110 if (message_center_bubble()) {
114 // Indicate that the message center is visible. Clears the unread count.
115 message_center_
->SetMessageCenterVisible(true);
118 message_center::MessageCenterBubble
* bubble
=
119 new message_center::MessageCenterBubble(message_center_
.get());
120 message_center_bubble_
.reset(
121 new internal::WebNotificationBubbleWrapper(this, bubble
));
123 status_area_widget()->SetHideSystemNotifications(true);
124 GetShelfLayoutManager()->UpdateAutoHideState();
127 void WebNotificationTray::HideMessageCenterBubble() {
128 if (!message_center_bubble())
130 message_center_bubble_
.reset();
131 show_message_center_on_unlock_
= false;
132 message_center_
->SetMessageCenterVisible(false);
134 status_area_widget()->SetHideSystemNotifications(false);
135 GetShelfLayoutManager()->UpdateAutoHideState();
138 void WebNotificationTray::SetHidePopupBubble(bool hide
) {
145 void WebNotificationTray::ShowPopupBubble() {
146 if (status_area_widget()->login_status() == user::LOGGED_IN_LOCKED
)
148 if (message_center_bubble())
150 if (!status_area_widget()->ShouldShowWebNotifications())
153 if (popup_bubble()) {
154 popup_bubble()->bubble()->ScheduleUpdate();
155 } else if (message_center_
->HasPopupNotifications()) {
157 new internal::WebNotificationBubbleWrapper(
158 this, new message_center::MessagePopupBubble(
159 message_center_
.get())));
163 void WebNotificationTray::HidePopupBubble() {
164 popup_bubble_
.reset();
167 bool WebNotificationTray::ShouldShowQuietModeBubble(const ui::Event
& event
) {
168 // TODO(mukai): Add keyboard event handler.
169 if (!event
.IsMouseEvent())
172 const ui::MouseEvent
* mouse_event
=
173 static_cast<const ui::MouseEvent
*>(&event
);
175 return mouse_event
->IsRightMouseButton();
178 void WebNotificationTray::ShowQuietModeBubble() {
179 aura::Window
* parent
= Shell::GetContainer(
180 Shell::GetPrimaryRootWindow(),
181 internal::kShellWindowId_SettingBubbleContainer
);
182 quiet_mode_bubble_
.reset(new message_center::QuietModeBubble(
183 button_
, parent
, message_center_
->notification_list()));
184 quiet_mode_bubble_
->GetBubbleWidget()->StackAtTop();
185 quiet_mode_bubble_
->GetBubbleWidget()->AddObserver(this);
188 void WebNotificationTray::UpdateAfterLoginStatusChange(
189 user::LoginStatus login_status
) {
190 if (login_status
== user::LOGGED_IN_LOCKED
) {
191 if (message_center_bubble()) {
192 message_center_bubble_
.reset();
193 show_message_center_on_unlock_
= true;
197 if (show_message_center_on_unlock_
)
198 ShowMessageCenterBubble();
199 show_message_center_on_unlock_
= false;
204 bool WebNotificationTray::IsMessageCenterBubbleVisible() const {
205 return (message_center_bubble() &&
206 message_center_bubble_
->bubble()->IsVisible());
209 bool WebNotificationTray::IsMouseInNotificationBubble() const {
212 return popup_bubble_
->bubble_view()->GetBoundsInScreen().Contains(
213 Shell::GetScreen()->GetCursorScreenPoint());
216 void WebNotificationTray::SetShelfAlignment(ShelfAlignment alignment
) {
217 if (alignment
== shelf_alignment())
219 internal::TrayBackgroundView::SetShelfAlignment(alignment
);
220 // Destroy any existing bubble so that it will be rebuilt correctly.
221 HideMessageCenterBubble();
225 void WebNotificationTray::AnchorUpdated() {
226 if (popup_bubble_
.get()) {
227 popup_bubble_
->bubble_view()->UpdateBubble();
228 // Ensure that the notification buble is above the launcher/status area.
229 popup_bubble_
->bubble_view()->GetWidget()->StackAtTop();
230 UpdateBubbleViewArrow(popup_bubble_
->bubble_view());
232 if (message_center_bubble_
.get()) {
233 message_center_bubble_
->bubble_view()->UpdateBubble();
234 UpdateBubbleViewArrow(message_center_bubble_
->bubble_view());
236 // Quiet mode settings bubble has to be on top.
237 if (quiet_mode_bubble() && quiet_mode_bubble_
->GetBubbleWidget())
238 quiet_mode_bubble_
->GetBubbleWidget()->StackAtTop();
241 string16
WebNotificationTray::GetAccessibleNameForTray() {
242 return l10n_util::GetStringUTF16(
243 IDS_ASH_WEB_NOTIFICATION_TRAY_ACCESSIBLE_NAME
);
246 void WebNotificationTray::HideBubbleWithView(
247 const views::TrayBubbleView
* bubble_view
) {
248 if (message_center_bubble() &&
249 bubble_view
== message_center_bubble()->bubble_view()) {
250 HideMessageCenterBubble();
251 } else if (popup_bubble() && bubble_view
== popup_bubble()->bubble_view()) {
256 bool WebNotificationTray::PerformAction(const ui::Event
& event
) {
257 if (!quiet_mode_bubble() && ShouldShowQuietModeBubble(event
)) {
258 ShowQuietModeBubble();
261 quiet_mode_bubble_
.reset();
262 ToggleMessageCenterBubble();
266 void WebNotificationTray::BubbleViewDestroyed() {
267 if (message_center_bubble())
268 message_center_bubble()->bubble()->BubbleViewDestroyed();
270 popup_bubble()->bubble()->BubbleViewDestroyed();
273 void WebNotificationTray::OnMouseEnteredView() {
275 popup_bubble()->bubble()->OnMouseEnteredView();
278 void WebNotificationTray::OnMouseExitedView() {
280 popup_bubble()->bubble()->OnMouseExitedView();
283 string16
WebNotificationTray::GetAccessibleNameForBubble() {
284 return GetAccessibleNameForTray();
287 gfx::Rect
WebNotificationTray::GetAnchorRect(views::Widget
* anchor_widget
,
288 AnchorType anchor_type
,
289 AnchorAlignment anchor_alignment
) {
290 return GetBubbleAnchorRect(anchor_widget
, anchor_type
, anchor_alignment
);
293 void WebNotificationTray::HideBubble(const views::TrayBubbleView
* bubble_view
) {
294 HideBubbleWithView(bubble_view
);
297 void WebNotificationTray::MessageCenterChanged(bool new_notification
) {
298 if (message_center_bubble()) {
299 if (message_center_
->NotificationCount() == 0)
300 HideMessageCenterBubble();
302 message_center_bubble()->bubble()->ScheduleUpdate();
304 if (popup_bubble()) {
305 if (message_center_
->NotificationCount() == 0)
308 popup_bubble()->bubble()->ScheduleUpdate();
311 if (new_notification
)
315 void WebNotificationTray::ButtonPressed(views::Button
* sender
,
316 const ui::Event
& event
) {
317 DCHECK_EQ(button_
, sender
);
318 PerformAction(event
);
321 void WebNotificationTray::OnWidgetClosing(views::Widget
* widget
) {
322 if (quiet_mode_bubble() && quiet_mode_bubble_
->GetBubbleWidget() == widget
) {
323 widget
->RemoveObserver(this);
325 quiet_mode_bubble_
.reset();
330 void WebNotificationTray::ToggleMessageCenterBubble() {
331 if (message_center_bubble())
332 HideMessageCenterBubble();
334 ShowMessageCenterBubble();
338 void WebNotificationTray::UpdateTray() {
339 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
340 if (message_center_
->UnreadNotificationCount() > 0) {
341 button_
->SetImage(views::CustomButton::STATE_NORMAL
, rb
.GetImageSkiaNamed(
342 IDR_AURA_UBER_TRAY_NOTIFY_BUTTON_ACTIVE_NORMAL
));
343 button_
->SetImage(views::CustomButton::STATE_HOVERED
, rb
.GetImageSkiaNamed(
344 IDR_AURA_UBER_TRAY_NOTIFY_BUTTON_ACTIVE_HOVER
));
345 button_
->SetImage(views::CustomButton::STATE_PRESSED
, rb
.GetImageSkiaNamed(
346 IDR_AURA_UBER_TRAY_NOTIFY_BUTTON_ACTIVE_PRESSED
));
348 button_
->SetImage(views::CustomButton::STATE_NORMAL
, rb
.GetImageSkiaNamed(
349 IDR_AURA_UBER_TRAY_NOTIFY_BUTTON_INACTIVE_NORMAL
));
350 button_
->SetImage(views::CustomButton::STATE_HOVERED
, rb
.GetImageSkiaNamed(
351 IDR_AURA_UBER_TRAY_NOTIFY_BUTTON_INACTIVE_HOVER
));
352 button_
->SetImage(views::CustomButton::STATE_PRESSED
, rb
.GetImageSkiaNamed(
353 IDR_AURA_UBER_TRAY_NOTIFY_BUTTON_INACTIVE_PRESSED
));
355 if (message_center_bubble())
356 button_
->SetState(views::CustomButton::STATE_PRESSED
);
358 button_
->SetState(views::CustomButton::STATE_NORMAL
);
360 (status_area_widget()->login_status() != user::LOGGED_IN_NONE
) &&
361 (status_area_widget()->login_status() != user::LOGGED_IN_LOCKED
) &&
362 (message_center_
->NotificationCount() > 0);
363 SetVisible(is_visible
);
368 bool WebNotificationTray::ClickedOutsideBubble() {
369 // Only hide the message center and quiet mode bubble.
370 if (!message_center_bubble() && !quiet_mode_bubble())
372 quiet_mode_bubble_
.reset();
373 HideMessageCenterBubble();
377 // Methods for testing
379 message_center::MessageCenterBubble
*
380 WebNotificationTray::GetMessageCenterBubbleForTest() {
381 if (!message_center_bubble_
.get())
383 return static_cast<message_center::MessageCenterBubble
*>(
384 message_center_bubble_
->bubble());
387 message_center::MessagePopupBubble
*
388 WebNotificationTray::GetPopupBubbleForTest() {
389 if (!popup_bubble_
.get())
391 return static_cast<message_center::MessagePopupBubble
*>(
392 popup_bubble_
->bubble());