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 "ash/system/tray/system_tray.h"
6
7 #include "ash/ash_switches.h"
8 #include "ash/metrics/user_metrics_recorder.h"
9 #include "ash/shelf/shelf_layout_manager.h"
10 #include "ash/shell.h"
11 #include "ash/shell_window_ids.h"
12 #include "ash/system/audio/tray_audio.h"
13 #include "ash/system/bluetooth/tray_bluetooth.h"
14 #include "ash/system/date/tray_date.h"
15 #include "ash/system/ime/tray_ime.h"
16 #include "ash/system/status_area_widget.h"
17 #include "ash/system/tray/system_tray_delegate.h"
18 #include "ash/system/tray/system_tray_item.h"
19 #include "ash/system/tray/tray_bubble_wrapper.h"
20 #include "ash/system/tray/tray_constants.h"
21 #include "ash/system/tray_accessibility.h"
22 #include "ash/system/tray_update.h"
23 #include "ash/system/user/login_status.h"
24 #include "ash/system/user/tray_user.h"
25 #include "ash/system/user/tray_user_separator.h"
26 #include "ash/system/web_notification/web_notification_tray.h"
27 #include "base/logging.h"
28 #include "base/strings/utf_string_conversions.h"
29 #include "base/timer/timer.h"
30 #include "grit/ash_strings.h"
31 #include "ui/aura/window_event_dispatcher.h"
32 #include "ui/base/l10n/l10n_util.h"
33 #include "ui/compositor/layer.h"
34 #include "ui/events/event_constants.h"
35 #include "ui/gfx/canvas.h"
36 #include "ui/gfx/screen.h"
37 #include "ui/gfx/skia_util.h"
38 #include "ui/views/border.h"
39 #include "ui/views/controls/label.h"
40 #include "ui/views/layout/box_layout.h"
41 #include "ui/views/layout/fill_layout.h"
42 #include "ui/views/view.h"
43
44 #if defined(OS_CHROMEOS)
45 #include "ash/system/chromeos/audio/tray_audio_chromeos.h"
46 #include "ash/system/chromeos/brightness/tray_brightness.h"
47 #include "ash/system/chromeos/enterprise/tray_enterprise.h"
48 #include "ash/system/chromeos/network/tray_network.h"
49 #include "ash/system/chromeos/network/tray_sms.h"
50 #include "ash/system/chromeos/network/tray_vpn.h"
51 #include "ash/system/chromeos/power/power_status.h"
52 #include "ash/system/chromeos/power/tray_power.h"
53 #include "ash/system/chromeos/rotation/tray_rotation_lock.h"
54 #include "ash/system/chromeos/screen_security/screen_capture_tray_item.h"
55 #include "ash/system/chromeos/screen_security/screen_share_tray_item.h"
56 #include "ash/system/chromeos/session/tray_session_length_limit.h"
57 #include "ash/system/chromeos/settings/tray_settings.h"
58 #include "ash/system/chromeos/supervised/tray_supervised_user.h"
59 #include "ash/system/chromeos/tray_caps_lock.h"
60 #include "ash/system/chromeos/tray_display.h"
61 #include "ash/system/chromeos/tray_tracing.h"
62 #include "ash/system/tray/media_security/multi_profile_media_tray_item.h"
63 #include "ui/message_center/message_center.h"
64 #elif defined(OS_WIN)
65 #include "ash/system/win/audio/tray_audio_win.h"
66 #include "media/audio/win/core_audio_util_win.h"
67 #endif
68
69 using views::TrayBubbleView;
70
71 namespace ash {
72
73 // The minimum width of the system tray menu width.
74 const int kMinimumSystemTrayMenuWidth = 300;
75
76 // Class to initialize and manage the SystemTrayBubble and TrayBubbleWrapper
77 // instances for a bubble.
78
79 class SystemBubbleWrapper {
80 public:
81 // Takes ownership of |bubble|.
SystemBubbleWrapper(SystemTrayBubble * bubble)82 explicit SystemBubbleWrapper(SystemTrayBubble* bubble)
83 : bubble_(bubble), is_persistent_(false) {}
84
85 // Initializes the bubble view and creates |bubble_wrapper_|.
InitView(TrayBackgroundView * tray,views::View * anchor,TrayBubbleView::InitParams * init_params,bool is_persistent)86 void InitView(TrayBackgroundView* tray,
87 views::View* anchor,
88 TrayBubbleView::InitParams* init_params,
89 bool is_persistent) {
90 DCHECK(anchor);
91 user::LoginStatus login_status =
92 Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus();
93 bubble_->InitView(anchor, login_status, init_params);
94 bubble_wrapper_.reset(new TrayBubbleWrapper(tray, bubble_->bubble_view()));
95 // The system bubble should not have an arrow.
96 bubble_->bubble_view()->SetArrowPaintType(
97 views::BubbleBorder::PAINT_NONE);
98 is_persistent_ = is_persistent;
99
100 // If ChromeVox is enabled, focus the default item if no item is focused.
101 if (Shell::GetInstance()->accessibility_delegate()->
102 IsSpokenFeedbackEnabled()) {
103 bubble_->FocusDefaultIfNeeded();
104 }
105 }
106
107 // Convenience accessors:
bubble() const108 SystemTrayBubble* bubble() const { return bubble_.get(); }
bubble_type() const109 SystemTrayBubble::BubbleType bubble_type() const {
110 return bubble_->bubble_type();
111 }
bubble_view() const112 TrayBubbleView* bubble_view() const { return bubble_->bubble_view(); }
is_persistent() const113 bool is_persistent() const { return is_persistent_; }
114
115 private:
116 scoped_ptr<SystemTrayBubble> bubble_;
117 scoped_ptr<TrayBubbleWrapper> bubble_wrapper_;
118 bool is_persistent_;
119
120 DISALLOW_COPY_AND_ASSIGN(SystemBubbleWrapper);
121 };
122
123
124 // SystemTray
125
SystemTray(StatusAreaWidget * status_area_widget)126 SystemTray::SystemTray(StatusAreaWidget* status_area_widget)
127 : TrayBackgroundView(status_area_widget),
128 items_(),
129 default_bubble_height_(0),
130 hide_notifications_(false),
131 full_system_tray_menu_(false),
132 tray_accessibility_(NULL),
133 tray_date_(NULL) {
134 SetContentsBackground();
135 }
136
~SystemTray()137 SystemTray::~SystemTray() {
138 // Destroy any child views that might have back pointers before ~View().
139 system_bubble_.reset();
140 notification_bubble_.reset();
141 for (std::vector<SystemTrayItem*>::iterator it = items_.begin();
142 it != items_.end();
143 ++it) {
144 (*it)->DestroyTrayView();
145 }
146 }
147
InitializeTrayItems(SystemTrayDelegate * delegate)148 void SystemTray::InitializeTrayItems(SystemTrayDelegate* delegate) {
149 TrayBackgroundView::Initialize();
150 CreateItems(delegate);
151 }
152
CreateItems(SystemTrayDelegate * delegate)153 void SystemTray::CreateItems(SystemTrayDelegate* delegate) {
154 #if !defined(OS_WIN)
155 // Create user items for each possible user.
156 ash::Shell* shell = ash::Shell::GetInstance();
157 int maximum_user_profiles =
158 shell->session_state_delegate()->GetMaximumNumberOfLoggedInUsers();
159 for (int i = 0; i < maximum_user_profiles; i++)
160 AddTrayItem(new TrayUser(this, i));
161
162 if (maximum_user_profiles > 1) {
163 // Add a special double line separator between users and the rest of the
164 // menu if more then one user is logged in.
165 AddTrayItem(new TrayUserSeparator(this));
166 }
167 #endif
168
169 tray_accessibility_ = new TrayAccessibility(this);
170 tray_date_ = new TrayDate(this);
171
172 #if defined(OS_CHROMEOS)
173 AddTrayItem(new TraySessionLengthLimit(this));
174 AddTrayItem(new TrayEnterprise(this));
175 AddTrayItem(new TraySupervisedUser(this));
176 AddTrayItem(new TrayIME(this));
177 AddTrayItem(tray_accessibility_);
178 AddTrayItem(new TrayTracing(this));
179 AddTrayItem(new TrayPower(this, message_center::MessageCenter::Get()));
180 AddTrayItem(new TrayNetwork(this));
181 AddTrayItem(new TrayVPN(this));
182 AddTrayItem(new TraySms(this));
183 AddTrayItem(new TrayBluetooth(this));
184 AddTrayItem(new TrayDisplay(this));
185 screen_capture_tray_item_ = new ScreenCaptureTrayItem(this);
186 AddTrayItem(screen_capture_tray_item_);
187 screen_share_tray_item_ = new ScreenShareTrayItem(this);
188 AddTrayItem(screen_share_tray_item_);
189 AddTrayItem(new MultiProfileMediaTrayItem(this));
190 AddTrayItem(new TrayAudioChromeOs(this));
191 AddTrayItem(new TrayBrightness(this));
192 AddTrayItem(new TrayCapsLock(this));
193 AddTrayItem(new TraySettings(this));
194 AddTrayItem(new TrayUpdate(this));
195 AddTrayItem(new TrayRotationLock(this));
196 AddTrayItem(tray_date_);
197 #elif defined(OS_WIN)
198 AddTrayItem(tray_accessibility_);
199 if (media::CoreAudioUtil::IsSupported())
200 AddTrayItem(new TrayAudioWin(this));
201 AddTrayItem(new TrayUpdate(this));
202 AddTrayItem(tray_date_);
203 #elif defined(OS_LINUX)
204 AddTrayItem(new TrayIME(this));
205 AddTrayItem(tray_accessibility_);
206 AddTrayItem(new TrayBluetooth(this));
207 AddTrayItem(new TrayUpdate(this));
208 AddTrayItem(tray_date_);
209 #endif
210
211 SetVisible(ash::Shell::GetInstance()->system_tray_delegate()->
212 GetTrayVisibilityOnStartup());
213 }
214
AddTrayItem(SystemTrayItem * item)215 void SystemTray::AddTrayItem(SystemTrayItem* item) {
216 items_.push_back(item);
217
218 SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate();
219 views::View* tray_item = item->CreateTrayView(delegate->GetUserLoginStatus());
220 item->UpdateAfterShelfAlignmentChange(shelf_alignment());
221
222 if (tray_item) {
223 tray_container()->AddChildViewAt(tray_item, 0);
224 PreferredSizeChanged();
225 tray_item_map_[item] = tray_item;
226 }
227 }
228
RemoveTrayItem(SystemTrayItem * item)229 void SystemTray::RemoveTrayItem(SystemTrayItem* item) {
230 NOTIMPLEMENTED();
231 }
232
GetTrayItems() const233 const std::vector<SystemTrayItem*>& SystemTray::GetTrayItems() const {
234 return items_.get();
235 }
236
ShowDefaultView(BubbleCreationType creation_type)237 void SystemTray::ShowDefaultView(BubbleCreationType creation_type) {
238 ShowDefaultViewWithOffset(
239 creation_type,
240 TrayBubbleView::InitParams::kArrowDefaultOffset,
241 false);
242 }
243
ShowPersistentDefaultView()244 void SystemTray::ShowPersistentDefaultView() {
245 ShowItems(items_.get(),
246 false,
247 false,
248 BUBBLE_CREATE_NEW,
249 TrayBubbleView::InitParams::kArrowDefaultOffset,
250 true);
251 }
252
ShowDetailedView(SystemTrayItem * item,int close_delay,bool activate,BubbleCreationType creation_type)253 void SystemTray::ShowDetailedView(SystemTrayItem* item,
254 int close_delay,
255 bool activate,
256 BubbleCreationType creation_type) {
257 std::vector<SystemTrayItem*> items;
258 // The detailed view with timeout means a UI to show the current system state,
259 // like the audio level or brightness. Such UI should behave as persistent and
260 // keep its own logic for the appearance.
261 bool persistent = (
262 !activate && close_delay > 0 && creation_type == BUBBLE_CREATE_NEW);
263 items.push_back(item);
264 ShowItems(
265 items, true, activate, creation_type, GetTrayXOffset(item), persistent);
266 if (system_bubble_)
267 system_bubble_->bubble()->StartAutoCloseTimer(close_delay);
268 }
269
SetDetailedViewCloseDelay(int close_delay)270 void SystemTray::SetDetailedViewCloseDelay(int close_delay) {
271 if (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DETAILED))
272 system_bubble_->bubble()->StartAutoCloseTimer(close_delay);
273 }
274
HideDetailedView(SystemTrayItem * item)275 void SystemTray::HideDetailedView(SystemTrayItem* item) {
276 if (item != detailed_item_)
277 return;
278 DestroySystemBubble();
279 UpdateNotificationBubble();
280 }
281
ShowNotificationView(SystemTrayItem * item)282 void SystemTray::ShowNotificationView(SystemTrayItem* item) {
283 if (std::find(notification_items_.begin(), notification_items_.end(), item)
284 != notification_items_.end())
285 return;
286 notification_items_.push_back(item);
287 UpdateNotificationBubble();
288 }
289
HideNotificationView(SystemTrayItem * item)290 void SystemTray::HideNotificationView(SystemTrayItem* item) {
291 std::vector<SystemTrayItem*>::iterator found_iter =
292 std::find(notification_items_.begin(), notification_items_.end(), item);
293 if (found_iter == notification_items_.end())
294 return;
295 notification_items_.erase(found_iter);
296 // Only update the notification bubble if visible (i.e. don't create one).
297 if (notification_bubble_)
298 UpdateNotificationBubble();
299 }
300
UpdateAfterLoginStatusChange(user::LoginStatus login_status)301 void SystemTray::UpdateAfterLoginStatusChange(user::LoginStatus login_status) {
302 DestroySystemBubble();
303 UpdateNotificationBubble();
304
305 for (std::vector<SystemTrayItem*>::iterator it = items_.begin();
306 it != items_.end();
307 ++it) {
308 (*it)->UpdateAfterLoginStatusChange(login_status);
309 }
310
311 // Items default to SHELF_ALIGNMENT_BOTTOM. Update them if the initial
312 // position of the shelf differs.
313 if (shelf_alignment() != SHELF_ALIGNMENT_BOTTOM)
314 UpdateAfterShelfAlignmentChange(shelf_alignment());
315
316 SetVisible(true);
317 PreferredSizeChanged();
318 }
319
UpdateAfterShelfAlignmentChange(ShelfAlignment alignment)320 void SystemTray::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
321 for (std::vector<SystemTrayItem*>::iterator it = items_.begin();
322 it != items_.end();
323 ++it) {
324 (*it)->UpdateAfterShelfAlignmentChange(alignment);
325 }
326 }
327
SetHideNotifications(bool hide_notifications)328 void SystemTray::SetHideNotifications(bool hide_notifications) {
329 if (notification_bubble_)
330 notification_bubble_->bubble()->SetVisible(!hide_notifications);
331 hide_notifications_ = hide_notifications;
332 }
333
ShouldShowShelf() const334 bool SystemTray::ShouldShowShelf() const {
335 return system_bubble_.get() && system_bubble_->bubble()->ShouldShowShelf();
336 }
337
HasSystemBubble() const338 bool SystemTray::HasSystemBubble() const {
339 return system_bubble_.get() != NULL;
340 }
341
HasNotificationBubble() const342 bool SystemTray::HasNotificationBubble() const {
343 return notification_bubble_.get() != NULL;
344 }
345
GetSystemBubble()346 SystemTrayBubble* SystemTray::GetSystemBubble() {
347 if (!system_bubble_)
348 return NULL;
349 return system_bubble_->bubble();
350 }
351
IsAnyBubbleVisible() const352 bool SystemTray::IsAnyBubbleVisible() const {
353 return ((system_bubble_.get() &&
354 system_bubble_->bubble()->IsVisible()) ||
355 (notification_bubble_.get() &&
356 notification_bubble_->bubble()->IsVisible()));
357 }
358
IsMouseInNotificationBubble() const359 bool SystemTray::IsMouseInNotificationBubble() const {
360 if (!notification_bubble_)
361 return false;
362 return notification_bubble_->bubble_view()->GetBoundsInScreen().Contains(
363 Shell::GetScreen()->GetCursorScreenPoint());
364 }
365
CloseSystemBubble() const366 bool SystemTray::CloseSystemBubble() const {
367 if (!system_bubble_)
368 return false;
369 system_bubble_->bubble()->Close();
370 return true;
371 }
372
GetHelpButtonView() const373 views::View* SystemTray::GetHelpButtonView() const {
374 return tray_date_->GetHelpButtonView();
375 }
376
CloseNotificationBubbleForTest() const377 bool SystemTray::CloseNotificationBubbleForTest() const {
378 if (!notification_bubble_)
379 return false;
380 notification_bubble_->bubble()->Close();
381 return true;
382 }
383
384 // Private methods.
385
HasSystemBubbleType(SystemTrayBubble::BubbleType type)386 bool SystemTray::HasSystemBubbleType(SystemTrayBubble::BubbleType type) {
387 DCHECK(type != SystemTrayBubble::BUBBLE_TYPE_NOTIFICATION);
388 return system_bubble_.get() && system_bubble_->bubble_type() == type;
389 }
390
DestroySystemBubble()391 void SystemTray::DestroySystemBubble() {
392 CloseSystemBubbleAndDeactivateSystemTray();
393 detailed_item_ = NULL;
394 UpdateWebNotifications();
395 }
396
DestroyNotificationBubble()397 void SystemTray::DestroyNotificationBubble() {
398 if (notification_bubble_) {
399 notification_bubble_.reset();
400 UpdateWebNotifications();
401 }
402 }
403
GetAccessibleNameForTray()404 base::string16 SystemTray::GetAccessibleNameForTray() {
405 base::string16 time = GetAccessibleTimeString(base::Time::Now());
406 base::string16 battery = base::ASCIIToUTF16("");
407 #if defined(OS_CHROMEOS)
408 battery = PowerStatus::Get()->GetAccessibleNameString(false);
409 #endif
410 return l10n_util::GetStringFUTF16(
411 IDS_ASH_STATUS_TRAY_ACCESSIBLE_DESCRIPTION, time, battery);
412 }
413
GetTrayXOffset(SystemTrayItem * item) const414 int SystemTray::GetTrayXOffset(SystemTrayItem* item) const {
415 // Don't attempt to align the arrow if the shelf is on the left or right.
416 if (shelf_alignment() != SHELF_ALIGNMENT_BOTTOM &&
417 shelf_alignment() != SHELF_ALIGNMENT_TOP)
418 return TrayBubbleView::InitParams::kArrowDefaultOffset;
419
420 std::map<SystemTrayItem*, views::View*>::const_iterator it =
421 tray_item_map_.find(item);
422 if (it == tray_item_map_.end())
423 return TrayBubbleView::InitParams::kArrowDefaultOffset;
424
425 const views::View* item_view = it->second;
426 if (item_view->bounds().IsEmpty()) {
427 // The bounds of item could be still empty if it does not have a visible
428 // tray view. In that case, use the default (minimum) offset.
429 return TrayBubbleView::InitParams::kArrowDefaultOffset;
430 }
431
432 gfx::Point point(item_view->width() / 2, 0);
433 ConvertPointToWidget(item_view, &point);
434 return point.x();
435 }
436
ShowDefaultViewWithOffset(BubbleCreationType creation_type,int arrow_offset,bool persistent)437 void SystemTray::ShowDefaultViewWithOffset(BubbleCreationType creation_type,
438 int arrow_offset,
439 bool persistent) {
440 if (creation_type != BUBBLE_USE_EXISTING) {
441 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
442 ash::UMA_STATUS_AREA_MENU_OPENED);
443 }
444 ShowItems(items_.get(), false, true, creation_type, arrow_offset, persistent);
445 }
446
ShowItems(const std::vector<SystemTrayItem * > & items,bool detailed,bool can_activate,BubbleCreationType creation_type,int arrow_offset,bool persistent)447 void SystemTray::ShowItems(const std::vector<SystemTrayItem*>& items,
448 bool detailed,
449 bool can_activate,
450 BubbleCreationType creation_type,
451 int arrow_offset,
452 bool persistent) {
453 // No system tray bubbles in kiosk mode.
454 if (Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus() ==
455 ash::user::LOGGED_IN_KIOSK_APP) {
456 return;
457 }
458
459 // Destroy any existing bubble and create a new one.
460 SystemTrayBubble::BubbleType bubble_type = detailed ?
461 SystemTrayBubble::BUBBLE_TYPE_DETAILED :
462 SystemTrayBubble::BUBBLE_TYPE_DEFAULT;
463
464 // Destroy the notification bubble here so that it doesn't get rebuilt
465 // while we add items to the main bubble_ (e.g. in HideNotificationView).
466 notification_bubble_.reset();
467 if (system_bubble_.get() && creation_type == BUBBLE_USE_EXISTING) {
468 system_bubble_->bubble()->UpdateView(items, bubble_type);
469 // If ChromeVox is enabled, focus the default item if no item is focused.
470 if (Shell::GetInstance()->accessibility_delegate()->
471 IsSpokenFeedbackEnabled()) {
472 system_bubble_->bubble()->FocusDefaultIfNeeded();
473 }
474 } else {
475 // Remember if the menu is a single property (like e.g. volume) or the
476 // full tray menu. Note that in case of the |BUBBLE_USE_EXISTING| case
477 // above, |full_system_tray_menu_| does not get changed since the fact that
478 // the menu is full (or not) doesn't change even if a "single property"
479 // (like network) replaces most of the menu.
480 full_system_tray_menu_ = items.size() > 1;
481 // The menu width is fixed, and it is a per language setting.
482 int menu_width = std::max(kMinimumSystemTrayMenuWidth,
483 Shell::GetInstance()->system_tray_delegate()->GetSystemTrayMenuWidth());
484
485 TrayBubbleView::InitParams init_params(TrayBubbleView::ANCHOR_TYPE_TRAY,
486 GetAnchorAlignment(),
487 menu_width,
488 kTrayPopupMaxWidth);
489 init_params.can_activate = can_activate;
490 init_params.first_item_has_no_margin = true;
491 if (detailed) {
492 // This is the case where a volume control or brightness control bubble
493 // is created.
494 init_params.max_height = default_bubble_height_;
495 init_params.arrow_color = kBackgroundColor;
496 } else {
497 init_params.arrow_color = kHeaderBackgroundColor;
498 }
499 init_params.arrow_offset = arrow_offset;
500 if (bubble_type == SystemTrayBubble::BUBBLE_TYPE_DEFAULT)
501 init_params.close_on_deactivate = !persistent;
502 // For Volume and Brightness we don't want to show an arrow when
503 // they are shown in a bubble by themselves.
504 init_params.arrow_paint_type = views::BubbleBorder::PAINT_NORMAL;
505 if (items.size() == 1 && items[0]->ShouldHideArrow())
506 init_params.arrow_paint_type = views::BubbleBorder::PAINT_TRANSPARENT;
507 SystemTrayBubble* bubble = new SystemTrayBubble(this, items, bubble_type);
508 system_bubble_.reset(new SystemBubbleWrapper(bubble));
509 system_bubble_->InitView(this, tray_container(), &init_params, persistent);
510 }
511 // Save height of default view for creating detailed views directly.
512 if (!detailed)
513 default_bubble_height_ = system_bubble_->bubble_view()->height();
514
515 if (detailed && items.size() > 0)
516 detailed_item_ = items[0];
517 else
518 detailed_item_ = NULL;
519
520 UpdateNotificationBubble(); // State changed, re-create notifications.
521 if (!notification_bubble_)
522 UpdateWebNotifications();
523 GetShelfLayoutManager()->UpdateAutoHideState();
524
525 // When we show the system menu in our alternate shelf layout, we need to
526 // tint the background.
527 if (full_system_tray_menu_)
528 SetDrawBackgroundAsActive(true);
529 }
530
UpdateNotificationBubble()531 void SystemTray::UpdateNotificationBubble() {
532 // Only show the notification bubble if we have notifications.
533 if (notification_items_.empty()) {
534 DestroyNotificationBubble();
535 return;
536 }
537 // Destroy the existing bubble before constructing a new one.
538 notification_bubble_.reset();
539 SystemTrayBubble* notification_bubble;
540 notification_bubble = new SystemTrayBubble(
541 this, notification_items_, SystemTrayBubble::BUBBLE_TYPE_NOTIFICATION);
542 views::View* anchor;
543 TrayBubbleView::AnchorType anchor_type;
544 // Tray items might want to show notifications while we are creating and
545 // initializing the |system_bubble_| - but it might not be fully initialized
546 // when coming here - this would produce a crashed like crbug.com/247416.
547 // As such we check the existence of the widget here.
548 if (system_bubble_.get() &&
549 system_bubble_->bubble_view() &&
550 system_bubble_->bubble_view()->GetWidget()) {
551 anchor = system_bubble_->bubble_view();
552 anchor_type = TrayBubbleView::ANCHOR_TYPE_BUBBLE;
553 } else {
554 anchor = tray_container();
555 anchor_type = TrayBubbleView::ANCHOR_TYPE_TRAY;
556 }
557 TrayBubbleView::InitParams init_params(anchor_type,
558 GetAnchorAlignment(),
559 kTrayPopupMinWidth,
560 kTrayPopupMaxWidth);
561 init_params.first_item_has_no_margin = true;
562 init_params.arrow_color = kBackgroundColor;
563 init_params.arrow_offset = GetTrayXOffset(notification_items_[0]);
564 notification_bubble_.reset(new SystemBubbleWrapper(notification_bubble));
565 notification_bubble_->InitView(this, anchor, &init_params, false);
566
567 if (notification_bubble->bubble_view()->child_count() == 0) {
568 // It is possible that none of the items generated actual notifications.
569 DestroyNotificationBubble();
570 return;
571 }
572 if (hide_notifications_)
573 notification_bubble->SetVisible(false);
574 else
575 UpdateWebNotifications();
576 }
577
UpdateWebNotifications()578 void SystemTray::UpdateWebNotifications() {
579 TrayBubbleView* bubble_view = NULL;
580 if (notification_bubble_)
581 bubble_view = notification_bubble_->bubble_view();
582 else if (system_bubble_)
583 bubble_view = system_bubble_->bubble_view();
584
585 int height = 0;
586 if (bubble_view) {
587 gfx::Rect work_area = Shell::GetScreen()->GetDisplayNearestWindow(
588 bubble_view->GetWidget()->GetNativeView()).work_area();
589 if (GetShelfLayoutManager()->GetAlignment() != SHELF_ALIGNMENT_TOP) {
590 height = std::max(
591 0, work_area.height() - bubble_view->GetBoundsInScreen().y());
592 } else {
593 height = std::max(
594 0, bubble_view->GetBoundsInScreen().bottom() - work_area.y());
595 }
596 }
597 status_area_widget()->web_notification_tray()->SetSystemTrayHeight(height);
598 }
599
GetAccessibleTimeString(const base::Time & now) const600 base::string16 SystemTray::GetAccessibleTimeString(
601 const base::Time& now) const {
602 base::HourClockType hour_type =
603 ash::Shell::GetInstance()->system_tray_delegate()->GetHourClockType();
604 return base::TimeFormatTimeOfDayWithHourClockType(
605 now, hour_type, base::kKeepAmPm);
606 }
607
SetShelfAlignment(ShelfAlignment alignment)608 void SystemTray::SetShelfAlignment(ShelfAlignment alignment) {
609 if (alignment == shelf_alignment())
610 return;
611 TrayBackgroundView::SetShelfAlignment(alignment);
612 UpdateAfterShelfAlignmentChange(alignment);
613 // Destroy any existing bubble so that it is rebuilt correctly.
614 CloseSystemBubbleAndDeactivateSystemTray();
615 // Rebuild any notification bubble.
616 if (notification_bubble_) {
617 notification_bubble_.reset();
618 UpdateNotificationBubble();
619 }
620 }
621
AnchorUpdated()622 void SystemTray::AnchorUpdated() {
623 if (notification_bubble_) {
624 notification_bubble_->bubble_view()->UpdateBubble();
625 // Ensure that the notification buble is above the shelf/status area.
626 notification_bubble_->bubble_view()->GetWidget()->StackAtTop();
627 UpdateBubbleViewArrow(notification_bubble_->bubble_view());
628 }
629 if (system_bubble_) {
630 system_bubble_->bubble_view()->UpdateBubble();
631 UpdateBubbleViewArrow(system_bubble_->bubble_view());
632 }
633 }
634
BubbleResized(const TrayBubbleView * bubble_view)635 void SystemTray::BubbleResized(const TrayBubbleView* bubble_view) {
636 UpdateWebNotifications();
637 }
638
HideBubbleWithView(const TrayBubbleView * bubble_view)639 void SystemTray::HideBubbleWithView(const TrayBubbleView* bubble_view) {
640 if (system_bubble_.get() && bubble_view == system_bubble_->bubble_view()) {
641 DestroySystemBubble();
642 UpdateNotificationBubble(); // State changed, re-create notifications.
643 GetShelfLayoutManager()->UpdateAutoHideState();
644 } else if (notification_bubble_.get() &&
645 bubble_view == notification_bubble_->bubble_view()) {
646 DestroyNotificationBubble();
647 }
648 }
649
ClickedOutsideBubble()650 bool SystemTray::ClickedOutsideBubble() {
651 if (!system_bubble_ || system_bubble_->is_persistent())
652 return false;
653 HideBubbleWithView(system_bubble_->bubble_view());
654 return true;
655 }
656
BubbleViewDestroyed()657 void SystemTray::BubbleViewDestroyed() {
658 if (system_bubble_) {
659 system_bubble_->bubble()->DestroyItemViews();
660 system_bubble_->bubble()->BubbleViewDestroyed();
661 }
662 }
663
OnMouseEnteredView()664 void SystemTray::OnMouseEnteredView() {
665 if (system_bubble_)
666 system_bubble_->bubble()->StopAutoCloseTimer();
667 }
668
OnMouseExitedView()669 void SystemTray::OnMouseExitedView() {
670 if (system_bubble_)
671 system_bubble_->bubble()->RestartAutoCloseTimer();
672 }
673
GetAccessibleNameForBubble()674 base::string16 SystemTray::GetAccessibleNameForBubble() {
675 return GetAccessibleNameForTray();
676 }
677
GetAnchorRect(views::Widget * anchor_widget,TrayBubbleView::AnchorType anchor_type,TrayBubbleView::AnchorAlignment anchor_alignment) const678 gfx::Rect SystemTray::GetAnchorRect(
679 views::Widget* anchor_widget,
680 TrayBubbleView::AnchorType anchor_type,
681 TrayBubbleView::AnchorAlignment anchor_alignment) const {
682 return GetBubbleAnchorRect(anchor_widget, anchor_type, anchor_alignment);
683 }
684
HideBubble(const TrayBubbleView * bubble_view)685 void SystemTray::HideBubble(const TrayBubbleView* bubble_view) {
686 HideBubbleWithView(bubble_view);
687 }
688
GetTrayItemViewForTest(SystemTrayItem * item)689 views::View* SystemTray::GetTrayItemViewForTest(SystemTrayItem* item) {
690 std::map<SystemTrayItem*, views::View*>::iterator it =
691 tray_item_map_.find(item);
692 return it == tray_item_map_.end() ? NULL : it->second;
693 }
694
GetTrayDateForTesting() const695 TrayDate* SystemTray::GetTrayDateForTesting() const { return tray_date_; }
696
PerformAction(const ui::Event & event)697 bool SystemTray::PerformAction(const ui::Event& event) {
698 // If we're already showing the default view, hide it; otherwise, show it
699 // (and hide any popup that's currently shown).
700 if (HasSystemBubbleType(SystemTrayBubble::BUBBLE_TYPE_DEFAULT)) {
701 system_bubble_->bubble()->Close();
702 } else {
703 int arrow_offset = TrayBubbleView::InitParams::kArrowDefaultOffset;
704 if (event.IsMouseEvent() || event.type() == ui::ET_GESTURE_TAP) {
705 const ui::LocatedEvent& located_event =
706 static_cast<const ui::LocatedEvent&>(event);
707 if (shelf_alignment() == SHELF_ALIGNMENT_BOTTOM ||
708 shelf_alignment() == SHELF_ALIGNMENT_TOP) {
709 gfx::Point point(located_event.x(), 0);
710 ConvertPointToWidget(this, &point);
711 arrow_offset = point.x();
712 }
713 }
714 ShowDefaultViewWithOffset(BUBBLE_CREATE_NEW, arrow_offset, false);
715 }
716 return true;
717 }
718
CloseSystemBubbleAndDeactivateSystemTray()719 void SystemTray::CloseSystemBubbleAndDeactivateSystemTray() {
720 system_bubble_.reset();
721 // When closing a system bubble with the alternate shelf layout, we need to
722 // turn off the active tinting of the shelf.
723 if (full_system_tray_menu_) {
724 SetDrawBackgroundAsActive(false);
725 full_system_tray_menu_ = false;
726 }
727 }
728
729 } // namespace ash
730