| // Copyright 2018 The Chromium Authors |
| // 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_controller.h" |
| |
| #include "ash/capture_mode/capture_mode_feature_pod_controller.h" |
| #include "ash/constants/ash_features.h" |
| #include "ash/constants/quick_settings_catalogs.h" |
| #include "ash/metrics/demo_session_metrics_recorder.h" |
| #include "ash/public/cpp/ash_view_ids.h" |
| #include "ash/public/cpp/metrics_util.h" |
| #include "ash/public/cpp/pagination/pagination_controller.h" |
| #include "ash/public/cpp/system_tray_client.h" |
| #include "ash/public/cpp/update_types.h" |
| #include "ash/session/session_controller_impl.h" |
| #include "ash/shell.h" |
| #include "ash/system/accessibility/accessibility_feature_pod_controller.h" |
| #include "ash/system/accessibility/unified_accessibility_detailed_view_controller.h" |
| #include "ash/system/audio/unified_audio_detailed_view_controller.h" |
| #include "ash/system/audio/unified_volume_slider_controller.h" |
| #include "ash/system/audio/unified_volume_view.h" |
| #include "ash/system/bluetooth/bluetooth_detailed_view_controller.h" |
| #include "ash/system/bluetooth/bluetooth_feature_pod_controller.h" |
| #include "ash/system/brightness/quick_settings_display_detailed_view_controller.h" |
| #include "ash/system/brightness/unified_brightness_slider_controller.h" |
| #include "ash/system/camera/autozoom_feature_pod_controller.h" |
| #include "ash/system/cast/cast_feature_pod_controller.h" |
| #include "ash/system/cast/unified_cast_detailed_view_controller.h" |
| #include "ash/system/dark_mode/dark_mode_feature_pod_controller.h" |
| #include "ash/system/focus_mode/focus_mode_detailed_view_controller.h" |
| #include "ash/system/focus_mode/focus_mode_feature_pod_controller.h" |
| #include "ash/system/hotspot/hotspot_detailed_view_controller.h" |
| #include "ash/system/hotspot/hotspot_feature_pod_controller.h" |
| #include "ash/system/ime/ime_feature_pod_controller.h" |
| #include "ash/system/ime/unified_ime_detailed_view_controller.h" |
| #include "ash/system/locale/locale_feature_pod_controller.h" |
| #include "ash/system/locale/unified_locale_detailed_view_controller.h" |
| #include "ash/system/media/media_tray.h" |
| #include "ash/system/media/unified_media_controls_detailed_view_controller.h" |
| #include "ash/system/model/clock_model.h" |
| #include "ash/system/model/system_tray_model.h" |
| #include "ash/system/model/update_model.h" |
| #include "ash/system/nearby_share/nearby_share_detailed_view_controller.h" |
| #include "ash/system/nearby_share/nearby_share_feature_pod_controller.h" |
| #include "ash/system/network/network_detailed_view_controller.h" |
| #include "ash/system/network/network_feature_pod_controller.h" |
| #include "ash/system/network/unified_vpn_detailed_view_controller.h" |
| #include "ash/system/network/vpn_feature_pod_controller.h" |
| #include "ash/system/night_light/night_light_feature_pod_controller.h" |
| #include "ash/system/privacy_screen/privacy_screen_feature_pod_controller.h" |
| #include "ash/system/rotation/rotation_lock_feature_pod_controller.h" |
| #include "ash/system/time/calendar_metrics.h" |
| #include "ash/system/time/unified_calendar_view_controller.h" |
| #include "ash/system/tray/tray_constants.h" |
| #include "ash/system/tray/tray_utils.h" |
| #include "ash/system/unified/deferred_update_dialog.h" |
| #include "ash/system/unified/detailed_view_controller.h" |
| #include "ash/system/unified/feature_pod_controller_base.h" |
| #include "ash/system/unified/feature_tile.h" |
| #include "ash/system/unified/feature_tiles_container_view.h" |
| #include "ash/system/unified/quick_settings_metrics_util.h" |
| #include "ash/system/unified/quick_settings_view.h" |
| #include "ash/system/unified/quiet_mode_feature_pod_controller.h" |
| #include "ash/system/unified/unified_system_tray_bubble.h" |
| #include "ash/system/unified/unified_system_tray_model.h" |
| #include "ash/system/unified/user_chooser_detailed_view_controller.h" |
| #include "ash/wm/lock_state_controller.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/metrics/user_metrics.h" |
| #include "components/global_media_controls/public/constants.h" |
| #include "media/base/media_switches.h" |
| #include "media/capture/video/chromeos/video_capture_features_chromeos.h" |
| #include "ui/accessibility/ax_enums.mojom.h" |
| #include "ui/compositor/compositor.h" |
| #include "ui/display/screen.h" |
| #include "ui/events/event.h" |
| #include "ui/views/widget/widget.h" |
| |
| namespace ash { |
| |
| // TODO(amehfooz): Add histograms for pagination metrics in system tray. |
| void RecordPageSwitcherSourceByEventType(ui::EventType type) {} |
| |
| UnifiedSystemTrayController::UnifiedSystemTrayController( |
| scoped_refptr<UnifiedSystemTrayModel> model, |
| UnifiedSystemTrayBubble* bubble, |
| views::View* owner_view) |
| : model_(model), bubble_(bubble) { |
| model_->pagination_model()->SetTransitionDurations(base::Milliseconds(250), |
| base::Milliseconds(50)); |
| |
| pagination_controller_ = std::make_unique<PaginationController>( |
| model_->pagination_model(), PaginationController::SCROLL_AXIS_HORIZONTAL, |
| base::BindRepeating(&RecordPageSwitcherSourceByEventType)); |
| } |
| |
| UnifiedSystemTrayController::~UnifiedSystemTrayController() = default; |
| |
| void UnifiedSystemTrayController::AddObserver(Observer* observer) { |
| if (observer) { |
| observers_.AddObserver(observer); |
| } |
| } |
| |
| void UnifiedSystemTrayController::RemoveObserver(Observer* observer) { |
| if (observer) { |
| observers_.RemoveObserver(observer); |
| } |
| } |
| |
| std::unique_ptr<QuickSettingsView> |
| UnifiedSystemTrayController::CreateQuickSettingsView(int max_height) { |
| DCHECK(!quick_settings_view_); |
| auto qs_view = std::make_unique<QuickSettingsView>(this); |
| quick_settings_view_ = qs_view.get(); |
| |
| if (!Shell::Get()->session_controller()->IsScreenLocked() && |
| !MediaTray::IsPinnedToShelf()) { |
| media_view_controller_ = |
| std::make_unique<QuickSettingsMediaViewController>(this); |
| qs_view->AddMediaView(media_view_controller_->CreateView()); |
| } |
| |
| volume_slider_controller_ = |
| std::make_unique<UnifiedVolumeSliderController>(this); |
| unified_volume_view_ = |
| qs_view->AddSliderView(volume_slider_controller_->CreateView()); |
| views::AsViewClass<QuickSettingsSlider>( |
| views::AsViewClass<UnifiedVolumeView>(unified_volume_view_)->slider()) |
| ->SetIsToggleableVolumeSlider(true); |
| |
| brightness_slider_controller_ = |
| std::make_unique<UnifiedBrightnessSliderController>( |
| model_, views::Button::PressedCallback(base::BindRepeating( |
| &UnifiedSystemTrayController::ShowDisplayDetailedView, |
| base::Unretained(this)))); |
| unified_brightness_view_ = |
| qs_view->AddSliderView(brightness_slider_controller_->CreateView()); |
| |
| qs_view->SetMaxHeight(max_height); |
| |
| // Feature Tiles are added last because the amount of rows depends on the |
| // available height. |
| InitFeatureTiles(); |
| |
| return qs_view; |
| } |
| |
| void UnifiedSystemTrayController::HandleSignOutAction() { |
| base::RecordAction(base::UserMetricsAction("StatusArea_SignOut")); |
| if (Shell::Get()->session_controller()->IsDemoSession()) { |
| DemoSessionMetricsRecorder::RecordExitSessionAction( |
| DemoSessionMetricsRecorder::ExitSessionFrom::kSystemTray); |
| } |
| |
| if (ShouldShowDeferredUpdateDialog()) { |
| DeferredUpdateDialog::CreateDialog( |
| DeferredUpdateDialog::Action::kSignOut, |
| base::BindOnce( |
| &LockStateController::RequestSignOut, |
| base::Unretained(Shell::Get()->lock_state_controller()))); |
| } else { |
| Shell::Get()->lock_state_controller()->RequestSignOut(); |
| } |
| } |
| |
| void UnifiedSystemTrayController::HandleLockAction() { |
| base::RecordAction(base::UserMetricsAction("Tray_LockScreen")); |
| Shell::Get()->session_controller()->LockScreen(); |
| } |
| |
| void UnifiedSystemTrayController::HandleSettingsAction() { |
| base::RecordAction(base::UserMetricsAction("Tray_Settings")); |
| Shell::Get()->system_tray_model()->client()->ShowSettings( |
| display::Screen::GetScreen() |
| ->GetDisplayNearestView( |
| quick_settings_view_->GetWidget()->GetNativeView()) |
| .id()); |
| } |
| |
| void UnifiedSystemTrayController::HandlePowerAction() { |
| base::RecordAction(base::UserMetricsAction("Tray_ShutDown")); |
| |
| if (ShouldShowDeferredUpdateDialog()) { |
| DeferredUpdateDialog::CreateDialog( |
| DeferredUpdateDialog::Action::kShutDown, |
| base::BindOnce(&LockStateController::RequestShutdown, |
| base::Unretained(Shell::Get()->lock_state_controller()), |
| ShutdownReason::TRAY_SHUT_DOWN_BUTTON)); |
| } else { |
| Shell::Get()->lock_state_controller()->RequestShutdown( |
| ShutdownReason::TRAY_SHUT_DOWN_BUTTON); |
| CloseBubble(); |
| } |
| } |
| |
| void UnifiedSystemTrayController::HandlePageSwitchAction(int page) { |
| // TODO(amehfooz) Record Pagination Metrics here. |
| model_->pagination_model()->SelectPage(page, true); |
| } |
| |
| void UnifiedSystemTrayController::HandleOpenDateTimeSettingsAction() { |
| ClockModel* model = Shell::Get()->system_tray_model()->clock(); |
| |
| if (Shell::Get()->session_controller()->ShouldEnableSettings()) { |
| model->ShowDateSettings(); |
| } else if (model->can_set_time()) { |
| model->ShowSetTimeDialog(); |
| } |
| } |
| |
| void UnifiedSystemTrayController::HandleOpenPowerSettingsAction() { |
| ClockModel* model = Shell::Get()->system_tray_model()->clock(); |
| |
| if (Shell::Get()->session_controller()->ShouldEnableSettings()) { |
| model->ShowPowerSettings(); |
| } |
| } |
| |
| void UnifiedSystemTrayController::HandleEnterpriseInfoAction() { |
| Shell::Get()->system_tray_model()->client()->ShowEnterpriseInfo(); |
| } |
| |
| void UnifiedSystemTrayController::ShowUserChooserView() { |
| if (!UserChooserDetailedViewController::IsUserChooserEnabled()) { |
| return; |
| } |
| ShowDetailedView(std::make_unique<UserChooserDetailedViewController>(this)); |
| } |
| |
| void UnifiedSystemTrayController::ShowNearbyShareDetailedView() { |
| ShowDetailedView(std::make_unique<NearbyShareDetailedViewController>(this)); |
| } |
| |
| void UnifiedSystemTrayController::ShowNetworkDetailedView() { |
| base::RecordAction(base::UserMetricsAction("StatusArea_Network_Detailed")); |
| ShowDetailedView(std::make_unique<NetworkDetailedViewController>(this)); |
| } |
| |
| void UnifiedSystemTrayController::ShowHotspotDetailedView() { |
| ShowDetailedView(std::make_unique<HotspotDetailedViewController>(this)); |
| } |
| |
| void UnifiedSystemTrayController::ShowBluetoothDetailedView() { |
| base::RecordAction(base::UserMetricsAction("StatusArea_Bluetooth_Detailed")); |
| ShowDetailedView(std::make_unique<BluetoothDetailedViewController>(this)); |
| } |
| |
| void UnifiedSystemTrayController::ShowCastDetailedView() { |
| base::RecordAction(base::UserMetricsAction("StatusArea_Cast_Detailed")); |
| ShowDetailedView(std::make_unique<UnifiedCastDetailedViewController>(this)); |
| } |
| |
| void UnifiedSystemTrayController::ShowAccessibilityDetailedView() { |
| base::RecordAction( |
| base::UserMetricsAction("StatusArea_Accessability_DetailedView")); |
| ShowDetailedView( |
| std::make_unique<UnifiedAccessibilityDetailedViewController>(this)); |
| showing_accessibility_detailed_view_ = true; |
| } |
| |
| void UnifiedSystemTrayController::ShowFocusModeDetailedView() { |
| base::RecordAction(base::UserMetricsAction("StatusArea_FocusMode_Detailed")); |
| ShowDetailedView(std::make_unique<FocusModeDetailedViewController>(this)); |
| } |
| |
| void UnifiedSystemTrayController::ShowVPNDetailedView() { |
| base::RecordAction(base::UserMetricsAction("StatusArea_VPN_Detailed")); |
| ShowDetailedView(std::make_unique<UnifiedVPNDetailedViewController>(this)); |
| } |
| |
| void UnifiedSystemTrayController::ShowIMEDetailedView() { |
| ShowDetailedView(std::make_unique<UnifiedIMEDetailedViewController>(this)); |
| } |
| |
| void UnifiedSystemTrayController::ShowLocaleDetailedView() { |
| ShowDetailedView(std::make_unique<UnifiedLocaleDetailedViewController>(this)); |
| } |
| |
| void UnifiedSystemTrayController::ShowAudioDetailedView() { |
| ShowDetailedView(std::make_unique<UnifiedAudioDetailedViewController>(this)); |
| showing_audio_detailed_view_ = true; |
| } |
| |
| void UnifiedSystemTrayController::ShowDisplayDetailedView() { |
| ShowDetailedView( |
| std::make_unique<QuickSettingsDisplayDetailedViewController>(this)); |
| showing_display_detailed_view_ = true; |
| } |
| |
| void UnifiedSystemTrayController::ShowCalendarView( |
| calendar_metrics::CalendarViewShowSource show_source, |
| calendar_metrics::CalendarEventSource event_source) { |
| calendar_metrics::RecordCalendarShowMetrics(show_source, event_source); |
| ShowDetailedView(std::make_unique<UnifiedCalendarViewController>()); |
| |
| showing_calendar_view_ = true; |
| showing_accessibility_detailed_view_ = false; |
| showing_audio_detailed_view_ = false; |
| showing_display_detailed_view_ = false; |
| |
| for (auto& observer : observers_) { |
| observer.OnOpeningCalendarView(); |
| } |
| } |
| |
| void UnifiedSystemTrayController::ShowMediaControlsDetailedView( |
| global_media_controls::GlobalMediaControlsEntryPoint entry_point, |
| const std::string& show_devices_for_item_id) { |
| ShowDetailedView(std::make_unique<UnifiedMediaControlsDetailedViewController>( |
| this, entry_point, show_devices_for_item_id)); |
| } |
| |
| void UnifiedSystemTrayController::TransitionToMainView(bool restore_focus) { |
| if (!detailed_view_controller_) { |
| return; |
| } |
| |
| if (showing_calendar_view_) { |
| showing_calendar_view_ = false; |
| for (auto& observer : observers_) { |
| observer.OnTransitioningFromCalendarToMainView(); |
| } |
| } |
| |
| showing_accessibility_detailed_view_ = false; |
| showing_audio_detailed_view_ = false; |
| showing_display_detailed_view_ = false; |
| |
| // Transfer `detailed_view_controller_` to a scoped object, which will be |
| // destroyed once it's out of this method's scope (after resetting |
| // `quick_settings_view_`'s `detailed_view_`). Because the detailed view has a |
| // reference to its `detailed_view_controller_` which is used in shutdown. |
| auto scoped_detailed_view_controller = std::move(detailed_view_controller_); |
| |
| bubble_->UpdateBubbleHeight(/*is_showing_detiled_view=*/false); |
| quick_settings_view_->ResetDetailedView(); |
| if (restore_focus) { |
| quick_settings_view_->RestoreFocus(); |
| } |
| UpdateBubble(); |
| } |
| |
| void UnifiedSystemTrayController::CloseBubble() { |
| if (quick_settings_view_->GetWidget()) { |
| quick_settings_view_->GetWidget()->CloseNow(); |
| } |
| } |
| |
| void UnifiedSystemTrayController::OnAudioSettingsButtonClicked() { |
| ShowAudioDetailedView(); |
| } |
| |
| void UnifiedSystemTrayController::SetShowMediaView(bool show_media_view) { |
| quick_settings_view_->SetShowMediaView(show_media_view); |
| } |
| |
| void UnifiedSystemTrayController::InitFeatureTiles() { |
| std::vector<std::unique_ptr<FeatureTile>> tiles; |
| |
| auto create_tile = |
| [](ViewID tile_id, std::unique_ptr<FeaturePodControllerBase> controller, |
| std::vector<std::unique_ptr<FeaturePodControllerBase>>& controllers, |
| std::vector<std::unique_ptr<FeatureTile>>& tiles, |
| bool compact = false) { |
| std::unique_ptr<FeatureTile> tile = controller->CreateTile(compact); |
| tile->SetID(static_cast<int>(tile_id)); |
| tiles.push_back(std::move(tile)); |
| controllers.push_back(std::move(controller)); |
| }; |
| |
| create_tile(VIEW_ID_FEATURE_TILE_NETWORK, |
| std::make_unique<NetworkFeaturePodController>(this), |
| feature_pod_controllers_, tiles); |
| |
| // CaptureMode and QuietMode tiles will be compact if both are visible. |
| bool capture_and_quiet_tiles_are_compact = |
| CaptureModeFeaturePodController::CalculateButtonVisibility() && |
| QuietModeFeaturePodController::CalculateButtonVisibility(); |
| create_tile(VIEW_ID_FEATURE_TILE_SCREEN_CAPTURE, |
| std::make_unique<CaptureModeFeaturePodController>(this), |
| feature_pod_controllers_, tiles, |
| capture_and_quiet_tiles_are_compact); |
| create_tile(VIEW_ID_FEATURE_TILE_DND, |
| std::make_unique<QuietModeFeaturePodController>(), |
| feature_pod_controllers_, tiles, |
| capture_and_quiet_tiles_are_compact); |
| create_tile(VIEW_ID_FEATURE_TILE_BLUETOOTH, |
| std::make_unique<BluetoothFeaturePodController>(this), |
| feature_pod_controllers_, tiles); |
| |
| // Cast and RotationLock tiles will be compact if both are visible. |
| bool cast_and_rotation_tiles_are_compact = |
| CastFeaturePodController::CalculateButtonVisibility() && |
| RotationLockFeaturePodController::CalculateButtonVisibility(); |
| create_tile(VIEW_ID_FEATURE_TILE_CAST, |
| std::make_unique<CastFeaturePodController>(this), |
| feature_pod_controllers_, tiles, |
| cast_and_rotation_tiles_are_compact); |
| create_tile(VIEW_ID_FEATURE_TILE_AUTOROTATE, |
| std::make_unique<RotationLockFeaturePodController>(), |
| feature_pod_controllers_, tiles, |
| cast_and_rotation_tiles_are_compact); |
| create_tile(VIEW_ID_FEATURE_TILE_ACCESSIBILITY, |
| std::make_unique<AccessibilityFeaturePodController>(this), |
| feature_pod_controllers_, tiles); |
| create_tile(VIEW_ID_FEATURE_TILE_HOTSPOT, |
| std::make_unique<HotspotFeaturePodController>(this), |
| feature_pod_controllers_, tiles); |
| create_tile(VIEW_ID_FEATURE_TILE_FOCUS_MODE, |
| std::make_unique<FocusModeFeaturePodController>(this), |
| feature_pod_controllers_, tiles); |
| create_tile(VIEW_ID_FEATURE_TILE_NEARBY_SHARE, |
| std::make_unique<NearbyShareFeaturePodController>(this), |
| feature_pod_controllers_, tiles); |
| create_tile(VIEW_ID_FEATURE_TILE_LOCALE, |
| std::make_unique<LocaleFeaturePodController>(this), |
| feature_pod_controllers_, tiles); |
| create_tile(VIEW_ID_FEATURE_TILE_IME, |
| std::make_unique<IMEFeaturePodController>(this), |
| feature_pod_controllers_, tiles); |
| if (media::ShouldEnableAutoFraming()) { |
| create_tile(VIEW_ID_FEATURE_TILE_AUTOZOOM, |
| std::make_unique<AutozoomFeaturePodController>(), |
| feature_pod_controllers_, tiles); |
| } |
| create_tile(VIEW_ID_FEATURE_TILE_VPN, |
| std::make_unique<VPNFeaturePodController>(this), |
| feature_pod_controllers_, tiles); |
| create_tile(VIEW_ID_FEATURE_TILE_PRIVACY_SCREEN, |
| std::make_unique<PrivacyScreenFeaturePodController>(), |
| feature_pod_controllers_, tiles); |
| |
| quick_settings_view_->AddTiles(std::move(tiles)); |
| |
| quick_settings_metrics_util::RecordQsFeaturePodCount( |
| quick_settings_view_->feature_tiles_container() |
| ->GetVisibleFeatureTileCount(), |
| display::Screen::GetScreen()->InTabletMode()); |
| } |
| |
| void UnifiedSystemTrayController::ShowDetailedView( |
| std::unique_ptr<DetailedViewController> controller) { |
| views::FocusManager* manager; |
| quick_settings_view_->SaveFocus(); |
| manager = quick_settings_view_->GetFocusManager(); |
| |
| if (manager && manager->GetFocusedView()) { |
| manager->ClearFocus(); |
| } |
| |
| showing_accessibility_detailed_view_ = false; |
| showing_audio_detailed_view_ = false; |
| showing_display_detailed_view_ = false; |
| bubble_->UpdateBubbleHeight(/*is_showing_detiled_view=*/true); |
| |
| ShutDownDetailedViewController(); |
| quick_settings_view_->SetDetailedView(controller->CreateView()); |
| |
| detailed_view_controller_ = std::move(controller); |
| |
| // `bubble_` may be null in tests. |
| if (bubble_) { |
| UpdateBubble(); |
| // Notify accessibility features that a new view is showing. |
| bubble_->NotifyAccessibilityEventDeprecated(ax::mojom::Event::kShow, true); |
| } |
| } |
| |
| bool UnifiedSystemTrayController::IsDetailedViewShown() const { |
| if (quick_settings_view_) { |
| return quick_settings_view_->IsDetailedViewShown(); |
| } |
| return false; |
| } |
| |
| void UnifiedSystemTrayController::UpdateBubble() { |
| if (!bubble_) { |
| return; |
| } |
| bubble_->UpdateBubble(); |
| } |
| |
| bool UnifiedSystemTrayController::ShouldShowDeferredUpdateDialog() const { |
| return Shell::Get()->system_tray_model()->update_model()->update_deferred() == |
| DeferredUpdateState::kShowDialog; |
| } |
| |
| void UnifiedSystemTrayController::ShutDownDetailedViewController() { |
| if (detailed_view_controller_) { |
| detailed_view_controller_->ShutDown(); |
| } |
| } |
| |
| } // namespace ash |