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