| // Copyright 2018 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/unified/unified_system_tray.h" |
| |
| #include "ash/accessibility/accessibility_controller.h" |
| #include "ash/public/cpp/ash_features.h" |
| #include "ash/session/session_controller_impl.h" |
| #include "ash/shelf/shelf.h" |
| #include "ash/shell.h" |
| #include "ash/strings/grit/ash_strings.h" |
| #include "ash/system/message_center/ash_popup_alignment_delegate.h" |
| #include "ash/system/message_center/message_center_ui_controller.h" |
| #include "ash/system/message_center/message_center_ui_delegate.h" |
| #include "ash/system/model/clock_model.h" |
| #include "ash/system/model/system_tray_model.h" |
| #include "ash/system/network/network_tray_view.h" |
| #include "ash/system/power/tray_power.h" |
| #include "ash/system/status_area_widget.h" |
| #include "ash/system/time/time_tray_item_view.h" |
| #include "ash/system/time/time_view.h" |
| #include "ash/system/tray/tray_constants.h" |
| #include "ash/system/tray/tray_container.h" |
| #include "ash/system/unified/current_locale_view.h" |
| #include "ash/system/unified/ime_mode_view.h" |
| #include "ash/system/unified/managed_device_tray_item_view.h" |
| #include "ash/system/unified/notification_counter_view.h" |
| #include "ash/system/unified/unified_slider_bubble_controller.h" |
| #include "ash/system/unified/unified_system_tray_bubble.h" |
| #include "ash/system/unified/unified_system_tray_model.h" |
| #include "base/time/time.h" |
| #include "base/timer/timer.h" |
| #include "chromeos/network/network_handler.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/display/display.h" |
| #include "ui/display/screen.h" |
| #include "ui/message_center/message_center.h" |
| #include "ui/message_center/views/message_popup_collection.h" |
| |
| namespace ash { |
| |
| class UnifiedSystemTray::UiDelegate : public MessageCenterUiDelegate { |
| public: |
| explicit UiDelegate(UnifiedSystemTray* owner); |
| ~UiDelegate() override; |
| |
| // MessageCenterUiDelegate: |
| void OnMessageCenterContentsChanged() override; |
| bool ShowPopups() override; |
| void HidePopups() override; |
| bool ShowMessageCenter(bool show_by_click) override; |
| void HideMessageCenter() override; |
| |
| MessageCenterUiController* ui_controller() { return ui_controller_.get(); } |
| |
| void SetTrayBubbleHeight(int height) { |
| popup_alignment_delegate_->SetTrayBubbleHeight(height); |
| } |
| message_center::MessagePopupView* GetPopupViewForNotificationID( |
| const std::string& notification_id) { |
| return message_popup_collection_->GetPopupViewForNotificationID( |
| notification_id); |
| } |
| |
| private: |
| std::unique_ptr<MessageCenterUiController> ui_controller_; |
| std::unique_ptr<AshPopupAlignmentDelegate> popup_alignment_delegate_; |
| std::unique_ptr<message_center::MessagePopupCollection> |
| message_popup_collection_; |
| |
| UnifiedSystemTray* const owner_; |
| |
| DISALLOW_COPY_AND_ASSIGN(UiDelegate); |
| }; |
| |
| const base::TimeDelta UnifiedSystemTray::kNotificationCountUpdateDelay = |
| base::TimeDelta::FromMilliseconds(100); |
| |
| UnifiedSystemTray::UiDelegate::UiDelegate(UnifiedSystemTray* owner) |
| : owner_(owner) { |
| ui_controller_ = std::make_unique<MessageCenterUiController>(this); |
| ui_controller_->set_hide_on_last_notification(false); |
| popup_alignment_delegate_ = |
| std::make_unique<AshPopupAlignmentDelegate>(owner->shelf()); |
| message_popup_collection_ = |
| std::make_unique<message_center::MessagePopupCollection>( |
| popup_alignment_delegate_.get()); |
| message_popup_collection_->set_inverse(); |
| display::Screen* screen = display::Screen::GetScreen(); |
| popup_alignment_delegate_->StartObserving( |
| screen, screen->GetDisplayNearestWindow( |
| owner->shelf()->GetStatusAreaWidget()->GetNativeWindow())); |
| } |
| |
| UnifiedSystemTray::UiDelegate::~UiDelegate() = default; |
| |
| void UnifiedSystemTray::UiDelegate::OnMessageCenterContentsChanged() { |
| owner_->UpdateNotificationInternal(); |
| } |
| |
| bool UnifiedSystemTray::UiDelegate::ShowPopups() { |
| if (owner_->IsBubbleShown()) |
| return false; |
| return true; |
| } |
| |
| void UnifiedSystemTray::UiDelegate::HidePopups() { |
| popup_alignment_delegate_->SetTrayBubbleHeight(0); |
| } |
| |
| bool UnifiedSystemTray::UiDelegate::ShowMessageCenter(bool show_by_click) { |
| if (owner_->IsBubbleShown()) |
| return false; |
| |
| owner_->ShowBubbleInternal(show_by_click); |
| return true; |
| } |
| |
| void UnifiedSystemTray::UiDelegate::HideMessageCenter() { |
| owner_->HideBubbleInternal(); |
| } |
| |
| UnifiedSystemTray::UnifiedSystemTray(Shelf* shelf) |
| : TrayBackgroundView(shelf), |
| ui_delegate_(std::make_unique<UiDelegate>(this)), |
| model_(std::make_unique<UnifiedSystemTrayModel>()), |
| slider_bubble_controller_( |
| std::make_unique<UnifiedSliderBubbleController>(this)), |
| current_locale_view_(new CurrentLocaleView(shelf)), |
| ime_mode_view_(new ImeModeView(shelf)), |
| managed_device_view_(new ManagedDeviceTrayItemView(shelf)), |
| notification_counter_item_(new NotificationCounterView(shelf)), |
| quiet_mode_view_(new QuietModeView(shelf)), |
| time_view_(new tray::TimeTrayItemView(shelf)) { |
| tray_container()->SetMargin(kUnifiedTrayContentPadding, 0); |
| tray_container()->AddChildView(current_locale_view_); |
| tray_container()->AddChildView(ime_mode_view_); |
| tray_container()->AddChildView(managed_device_view_); |
| tray_container()->AddChildView(notification_counter_item_); |
| tray_container()->AddChildView(quiet_mode_view_); |
| |
| if (features::IsSeparateNetworkIconsEnabled()) { |
| tray_container()->AddChildView( |
| new tray::NetworkTrayView(shelf, ActiveNetworkIcon::Type::kPrimary)); |
| tray_container()->AddChildView( |
| new tray::NetworkTrayView(shelf, ActiveNetworkIcon::Type::kCellular)); |
| } else { |
| tray_container()->AddChildView( |
| new tray::NetworkTrayView(shelf, ActiveNetworkIcon::Type::kSingle)); |
| } |
| |
| tray_container()->AddChildView(new tray::PowerTrayView(shelf)); |
| tray_container()->AddChildView(time_view_); |
| |
| SetInkDropMode(InkDropMode::ON); |
| set_separator_visibility(false); |
| } |
| |
| UnifiedSystemTray::~UnifiedSystemTray() { |
| // Close bubble immediately when the bubble is closed on dtor. |
| if (bubble_) |
| bubble_->CloseNow(); |
| bubble_.reset(); |
| } |
| |
| bool UnifiedSystemTray::IsBubbleShown() const { |
| return !!bubble_; |
| } |
| |
| bool UnifiedSystemTray::IsSliderBubbleShown() const { |
| return slider_bubble_controller_->IsBubbleShown(); |
| } |
| |
| bool UnifiedSystemTray::IsBubbleActive() const { |
| return bubble_ && bubble_->IsBubbleActive(); |
| } |
| |
| void UnifiedSystemTray::ActivateBubble() { |
| if (bubble_) |
| bubble_->ActivateBubble(); |
| } |
| |
| void UnifiedSystemTray::EnsureBubbleExpanded() { |
| if (bubble_) |
| bubble_->EnsureExpanded(); |
| } |
| |
| void UnifiedSystemTray::ShowVolumeSliderBubble() { |
| slider_bubble_controller_->ShowBubble( |
| UnifiedSliderBubbleController::SLIDER_TYPE_VOLUME); |
| } |
| |
| void UnifiedSystemTray::ShowAudioDetailedViewBubble() { |
| ShowBubble(false /* show_by_click */); |
| bubble_->ShowAudioDetailedView(); |
| } |
| |
| void UnifiedSystemTray::SetTrayBubbleHeight(int height) { |
| ui_delegate_->SetTrayBubbleHeight(height); |
| } |
| |
| gfx::Rect UnifiedSystemTray::GetBubbleBoundsInScreen() const { |
| return bubble_ ? bubble_->GetBoundsInScreen() : gfx::Rect(); |
| } |
| |
| void UnifiedSystemTray::UpdateAfterLoginStatusChange() { |
| SetVisible(true); |
| PreferredSizeChanged(); |
| } |
| |
| bool UnifiedSystemTray::ShouldEnableExtraKeyboardAccessibility() { |
| return Shell::Get()->accessibility_controller()->spoken_feedback_enabled(); |
| } |
| |
| void UnifiedSystemTray::AddInkDropLayer(ui::Layer* ink_drop_layer) { |
| TrayBackgroundView::AddInkDropLayer(ink_drop_layer); |
| ink_drop_layer_ = ink_drop_layer; |
| } |
| |
| void UnifiedSystemTray::RemoveInkDropLayer(ui::Layer* ink_drop_layer) { |
| DCHECK_EQ(ink_drop_layer, ink_drop_layer_); |
| TrayBackgroundView::RemoveInkDropLayer(ink_drop_layer); |
| ink_drop_layer_ = nullptr; |
| } |
| |
| void UnifiedSystemTray::OnBoundsChanged(const gfx::Rect& previous_bounds) { |
| TrayBackgroundView::OnBoundsChanged(previous_bounds); |
| // Workarounding an ui::Layer bug that layer mask is not properly updated. |
| // https://crbug.com/860367 |
| // TODO(tetsui): Remove after the bug is fixed on ui::Layer side. |
| if (ink_drop_layer_) { |
| ResetInkDropMask(); |
| InstallInkDropMask(ink_drop_layer_); |
| } |
| } |
| |
| void UnifiedSystemTray::SetTrayEnabled(bool enabled) { |
| // We should close bubble at this point. If it remains opened and interactive, |
| // it can be dangerous (http://crbug.com/497080). |
| if (!enabled && bubble_) |
| CloseBubble(); |
| |
| SetEnabled(enabled); |
| } |
| |
| void UnifiedSystemTray::SetTargetNotification( |
| const std::string& notification_id) { |
| model_->SetTargetNotification(notification_id); |
| } |
| |
| bool UnifiedSystemTray::PerformAction(const ui::Event& event) { |
| if (bubble_) { |
| CloseBubble(); |
| } else { |
| ShowBubble(event.IsMouseEvent() || event.IsGestureEvent()); |
| if (event.IsKeyEvent() || (event.flags() & ui::EF_TOUCH_ACCESSIBILITY)) |
| ActivateBubble(); |
| } |
| return true; |
| } |
| |
| void UnifiedSystemTray::ShowBubble(bool show_by_click) { |
| // ShowBubbleInternal will be called from UiDelegate. |
| if (!bubble_) |
| ui_delegate_->ui_controller()->ShowMessageCenterBubble(show_by_click); |
| } |
| |
| void UnifiedSystemTray::CloseBubble() { |
| // HideBubbleInternal will be called from UiDelegate. |
| ui_delegate_->ui_controller()->HideMessageCenterBubble(); |
| } |
| |
| base::string16 UnifiedSystemTray::GetAccessibleNameForBubble() { |
| return GetAccessibleNameForTray(); |
| } |
| |
| base::string16 UnifiedSystemTray::GetAccessibleNameForTray() { |
| base::string16 time = base::TimeFormatTimeOfDayWithHourClockType( |
| base::Time::Now(), |
| Shell::Get()->system_tray_model()->clock()->hour_clock_type(), |
| base::kKeepAmPm); |
| base::string16 battery = PowerStatus::Get()->GetAccessibleNameString(false); |
| return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBLE_DESCRIPTION, |
| time, battery); |
| } |
| |
| void UnifiedSystemTray::HideBubble(const TrayBubbleView* bubble_view) { |
| CloseBubble(); |
| } |
| |
| void UnifiedSystemTray::HideBubbleWithView(const TrayBubbleView* bubble_view) {} |
| |
| void UnifiedSystemTray::ClickedOutsideBubble() { |
| CloseBubble(); |
| } |
| |
| void UnifiedSystemTray::UpdateAfterShelfAlignmentChange() { |
| TrayBackgroundView::UpdateAfterShelfAlignmentChange(); |
| time_view_->UpdateAlignmentForShelf(shelf()); |
| } |
| |
| void UnifiedSystemTray::ShowBubbleInternal(bool show_by_click) { |
| // Never show System Tray bubble in kiosk app mode. |
| if (Shell::Get()->session_controller()->IsRunningInAppMode()) |
| return; |
| |
| // Hide volume/brightness slider popup. |
| slider_bubble_controller_->CloseBubble(); |
| |
| bubble_ = std::make_unique<UnifiedSystemTrayBubble>(this, show_by_click); |
| SetIsActive(true); |
| } |
| |
| void UnifiedSystemTray::HideBubbleInternal() { |
| bubble_.reset(); |
| SetIsActive(false); |
| } |
| |
| void UnifiedSystemTray::UpdateNotificationInternal() { |
| // Limit update frequency in order to avoid flashing when 2 updates are |
| // incoming in a very short period of time. It happens when ARC++ apps |
| // creating bundled notifications. |
| if (!timer_.IsRunning()) { |
| timer_.Start(FROM_HERE, kNotificationCountUpdateDelay, this, |
| &UnifiedSystemTray::UpdateNotificationAfterDelay); |
| } |
| } |
| |
| void UnifiedSystemTray::UpdateNotificationAfterDelay() { |
| notification_counter_item_->Update(); |
| quiet_mode_view_->Update(); |
| } |
| |
| message_center::MessagePopupView* |
| UnifiedSystemTray::GetPopupViewForNotificationID( |
| const std::string& notification_id) { |
| return ui_delegate_->GetPopupViewForNotificationID(notification_id); |
| } |
| |
| } // namespace ash |