0

[QsRevamp Clean Up] remove the use of UnifiedSystemTrayView

Also removed the sub views of it.

Bug: b/305075031
Change-Id: I9b47f8205fec0c32e94ff85dfac14ed672271674
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5009234
Reviewed-by: Alex Newcomer <newcomer@chromium.org>
Reviewed-by: James Cook <jamescook@chromium.org>
Commit-Queue: Jiaming Cheng <jiamingc@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1221177}
This commit is contained in:
Jiaming Cheng
2023-11-07 21:05:44 +00:00
committed by Chromium LUCI CQ
parent 883ab6ffe5
commit 2480e206df
53 changed files with 121 additions and 4569 deletions
ash
BUILD.gnash_prefs.ccash_strings.grd
ash_strings_grd
system
accessibility
bluetooth
channel_indicator
message_center
network
privacy_screen
time
tray
unified
chrome/browser/ui/ash

@ -2171,8 +2171,6 @@ component("ash") {
"system/unified/feature_pod_button.h",
"system/unified/feature_pod_controller_base.cc",
"system/unified/feature_pod_controller_base.h",
"system/unified/feature_pods_container_view.cc",
"system/unified/feature_pods_container_view.h",
"system/unified/feature_tile.cc",
"system/unified/feature_tile.h",
"system/unified/feature_tiles_container_view.cc",
@ -2189,12 +2187,8 @@ component("ash") {
"system/unified/managed_device_tray_item_view.h",
"system/unified/notification_counter_view.cc",
"system/unified/notification_counter_view.h",
"system/unified/notification_hidden_view.cc",
"system/unified/notification_hidden_view.h",
"system/unified/notification_icons_controller.cc",
"system/unified/notification_icons_controller.h",
"system/unified/page_indicator_view.cc",
"system/unified/page_indicator_view.h",
"system/unified/power_button.cc",
"system/unified/power_button.h",
"system/unified/quick_settings_footer.cc",
@ -2215,16 +2209,12 @@ component("ash") {
"system/unified/tasks_bubble_view.h",
"system/unified/tasks_combobox_model.cc",
"system/unified/tasks_combobox_model.h",
"system/unified/top_shortcuts_view.cc",
"system/unified/top_shortcuts_view.h",
"system/unified/unified_notifier_settings_controller.cc",
"system/unified/unified_notifier_settings_controller.h",
"system/unified/unified_slider_bubble_controller.cc",
"system/unified/unified_slider_bubble_controller.h",
"system/unified/unified_slider_view.cc",
"system/unified/unified_slider_view.h",
"system/unified/unified_system_info_view.cc",
"system/unified/unified_system_info_view.h",
"system/unified/unified_system_tray.cc",
"system/unified/unified_system_tray.h",
"system/unified/unified_system_tray_bubble.cc",
@ -2233,8 +2223,6 @@ component("ash") {
"system/unified/unified_system_tray_controller.h",
"system/unified/unified_system_tray_model.cc",
"system/unified/unified_system_tray_model.h",
"system/unified/unified_system_tray_view.cc",
"system/unified/unified_system_tray_view.h",
"system/unified/user_chooser_detailed_view_controller.cc",
"system/unified/user_chooser_detailed_view_controller.h",
"system/unified/user_chooser_view.cc",
@ -3659,12 +3647,10 @@ test("ash_unittests") {
"system/tray/tri_view_unittest.cc",
"system/unified/classroom_bubble_view_unittest.cc",
"system/unified/date_tray_unittest.cc",
"system/unified/feature_pods_container_view_unittest.cc",
"system/unified/feature_tile_unittest.cc",
"system/unified/feature_tiles_container_view_unittest.cc",
"system/unified/notification_counter_view_unittest.cc",
"system/unified/notification_icons_controller_unittest.cc",
"system/unified/page_indicator_view_unittest.cc",
"system/unified/power_button_unittest.cc",
"system/unified/quick_settings_footer_unittest.cc",
"system/unified/quick_settings_header_unittest.cc",
@ -3673,7 +3659,6 @@ test("ash_unittests") {
"system/unified/screen_capture_tray_item_view_unittest.cc",
"system/unified/tasks_bubble_view_unittest.cc",
"system/unified/tasks_combobox_model_unittest.cc",
"system/unified/unified_system_info_view_unittest.cc",
"system/unified/unified_system_tray_controller_unittest.cc",
"system/unified/unified_system_tray_unittest.cc",
"system/unified/user_chooser_detailed_view_controller_unittest.cc",

@ -66,7 +66,6 @@
#include "ash/system/session/logout_button_tray.h"
#include "ash/system/session/logout_confirmation_controller.h"
#include "ash/system/unified/quick_settings_footer.h"
#include "ash/system/unified/top_shortcuts_view.h"
#include "ash/system/unified/unified_system_tray_controller.h"
#include "ash/system/usb_peripheral/usb_peripheral_notification_controller.h"
#include "ash/touch/touch_devices_controller.h"
@ -200,11 +199,7 @@ void RegisterLocalStatePrefs(PrefRegistrySimple* registry, bool for_test) {
LoginExpandedPublicAccountView::RegisterLocalStatePrefs(registry);
LockStateController::RegisterPrefs(registry);
quick_pair::Mediator::RegisterLocalStatePrefs(registry);
if (ash::features::IsQsRevampEnabled()) {
QuickSettingsFooter::RegisterLocalStatePrefs(registry);
} else {
TopShortcutsView::RegisterLocalStatePrefs(registry);
}
QuickSettingsFooter::RegisterLocalStatePrefs(registry);
KeyboardBacklightColorController::RegisterPrefs(registry);
BatterySaverController::RegisterLocalStatePrefs(registry);
PowerSoundsController::RegisterLocalStatePrefs(registry);

@ -310,9 +310,6 @@ Style notes:
<ph name="network">$2<ex>Connected to Google-A, Median signal</ex></ph>
</message>
<message name="IDS_ASH_QUICK_SETTINGS_BUBBLE_ACCESSIBLE_DESCRIPTION" desc="The accessible description of the quick settings bubble and the information in it.">
Quick Settings, Press search + left to access the notification center.
</message>
<message name="IDS_ASH_REVAMPED_QUICK_SETTINGS_BUBBLE_ACCESSIBLE_DESCRIPTION" desc="The accessible description of the revamped quick settings bubble and the information in it.">
Quick Settings
</message>
<message name="IDS_ASH_QUICK_SETTINGS_BUBBLE_A11Y_SETTINGS_ACCESSIBLE_DESCRIPTION" desc="The accessible description for accessibility settings accessed through the accessibility feature pod.">
@ -1390,9 +1387,6 @@ Style notes:
<message name="IDS_ASH_STATUS_TRAY_HELP" desc="The accessible text for the help button.">
Help
</message>
<message name="IDS_ASH_STATUS_TRAY_DATE" desc="The date displayed on ash system bubble, Depending on launguage, please choose the best separator(eg ',') between abbreviated weekday and date">
<ph name="short_weekday">$1<ex>Fri</ex></ph>, <ph name="date">$2<ex>Aug 31, 2012</ex></ph>
</message>
<message name="IDS_ASH_STATUS_TRAY_DATE_TIME" desc="The time displayed on ash system tray, Depending on launguage, please choose the best separator(eg ',') between abbreviated date and time">
<ph name="date">$1<ex>Aug 31</ex></ph>, <ph name="time">$2<ex>03:00</ex></ph>
</message>
@ -5070,15 +5064,6 @@ Some features are limited to increase battery life.
<message name="IDS_ASH_MESSAGE_CENTER_FOOTER_LOCKSCREEN" desc="The label in the footer of the message center on lock screen">
Unlock device to view your notifications
</message>
<message name="IDS_ASH_MESSAGE_CENTER_LOCKSCREEN_UNIFIED" desc="The label in the message center area on lock screen.">
Notifications are hidden.
</message>
<message name="IDS_ASH_MESSAGE_CENTER_LOCKSCREEN_CHANGE" desc="The label in the button in the message center area on lock screen. This button is to open the setting of the lock scren notification.">
Change
</message>
<message name="IDS_ASH_MESSAGE_CENTER_LOCKSCREEN_CHANGE_TOOLTIP" desc="The tooltip text for Change button to open the setting of lock screen notification.">
Change the lock-screen notification settings
</message>
<message name="IDS_ASH_MESSAGE_CENTER_NO_MESSAGES" desc="The message displayed in the message center when there are no notifications.">
All done
</message>
@ -5179,10 +5164,6 @@ Some features are limited to increase battery life.
Calendar, <ph name="current_month_year">$1<ex>Wednesday, August 17, 2022</ex></ph>
</message>
<message name="IDS_ASH_CALENDAR_ENTRY_ACCESSIBLE_DESCRIPTION" desc="The accessible description of the calendar entry button.">
<ph name="current_time">$1<ex>Tuesday, August 31, 2021 at 11:25:12 AM</ex></ph>, Press enter key to open Calendar view
</message>
<message name="IDS_ASH_CALENDAR_TITLE" desc="The label used as the title of the unified system tray calendar view.">
Calendar
</message>
@ -5686,9 +5667,6 @@ Here are some things you can try to get started.
<message name="IDS_ASH_MESSAGE_CENTER_UNLOCK_TO_PERFORM_ACTION_WITH_USER_ID" desc="The short message to encourage user to unlock the device so that ChromeOS can perform the notification action selected by user after unlocking.">
Unlock device as <ph name="login_id">$1<ex>example@gmail.com</ex></ph> to perform the notification action
</message>
<message name="IDS_ASH_MESSAGE_CENTER_UNLOCK_TO_CHANGE_SETTING" desc="The short message to encourage user to unlock the device to change the lock screen notification setting, so that ChromeOS can show the setting window after unlocking.">
To see notifications on your lock screen, unlock to change the setting
</message>
<!-- Media Notification -->
<message name="IDS_ASH_MEDIA_NOTIFICATION_ACTION_PREVIOUS_TRACK" desc="The button to trigger media playback to go to the previous track.">

@ -1 +0,0 @@
f1e17f22d50bbacb0252fb395913043eba44301b

@ -1 +0,0 @@
e09cf86b689a4c2f1b897737df6f9a229b66944f

@ -6,7 +6,6 @@
#include "ash/system/unified/unified_system_tray.h"
#include "ash/system/unified/unified_system_tray_bubble.h"
#include "ash/system/unified/unified_system_tray_controller.h"
#include "ash/system/unified/unified_system_tray_view.h"
#include "ash/test/ash_test_base.h"
#include "ash/test/pixel/ash_pixel_differ.h"
#include "ash/test/pixel/ash_pixel_test_init_params.h"

@ -14,7 +14,6 @@
#include "ash/system/tray/tray_background_view.h"
#include "ash/system/tray/tray_bubble_view.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/unified/unified_system_tray_view.h"
#include "ash/wm/collision_detection/collision_detection_utils.h"
#include "ash/wm/work_area_insets.h"
#include "ash/wm/workspace/workspace_layout_manager.h"

@ -11,7 +11,6 @@
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/tray/tray_background_view.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/unified/unified_system_tray_view.h"
#include "ash/wm/collision_detection/collision_detection_utils.h"
#include "ash/wm/work_area_insets.h"
#include "ash/wm/workspace/workspace_layout_manager.h"

@ -13,7 +13,6 @@
#include "ash/system/accessibility/select_to_speak/select_to_speak_constants.h"
#include "ash/system/tray/tray_background_view.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/unified/unified_system_tray_view.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/compositor/layer.h"
#include "ui/views/border.h"

@ -15,7 +15,6 @@
#include "ash/system/accessibility/select_to_speak/select_to_speak_speed_view.h"
#include "ash/system/tray/tray_background_view.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/unified/unified_system_tray_view.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/compositor/layer.h"
#include "ui/wm/public/activation_client.h"

@ -11,7 +11,6 @@
#include "ash/system/accessibility/switch_access/switch_access_menu_view.h"
#include "ash/system/tray/tray_background_view.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/unified/unified_system_tray_view.h"
#include "ash/wm/collision_detection/collision_detection_utils.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/compositor/layer.h"

@ -18,7 +18,6 @@
#include "ash/system/unified/unified_system_tray.h"
#include "ash/system/unified/unified_system_tray_bubble.h"
#include "ash/system/unified/unified_system_tray_controller.h"
#include "ash/system/unified/unified_system_tray_view.h"
#include "ash/test/ash_test_base.h"
#include "ash/test/ash_test_helper.h"
#include "base/i18n/number_formatting.h"
@ -214,10 +213,6 @@ class BluetoothFeaturePodControllerTest : public AshTestBase {
->unified_system_tray_controller();
}
UnifiedSystemTrayView* tray_view() {
return GetPrimaryUnifiedSystemTray()->bubble()->unified_view();
}
size_t GetTryToShowSurveyCount() {
return fake_trigger_impl_->try_to_show_survey_count();
}

@ -14,7 +14,7 @@
namespace ash {
// ChannelIndicatorQuickSettingsView contains all of the views included in the
// channel indicator UI that resides in UnifiedSystemInfoView.
// channel indicator UI that resides in `QuickSettingsHeader`.
class ASH_EXPORT ChannelIndicatorQuickSettingsView : public views::View {
public:
METADATA_HEADER(ChannelIndicatorQuickSettingsView);

@ -44,15 +44,6 @@ class AshMessageCenterLockScreenController
bool IsScreenLocked() const override;
private:
FRIEND_TEST_ALL_PREFIXES(UnifiedSystemTrayControllerTest,
NotificationHiddenView_ModeShow);
FRIEND_TEST_ALL_PREFIXES(UnifiedSystemTrayControllerTest,
NotificationHiddenView_ModeHide);
FRIEND_TEST_ALL_PREFIXES(UnifiedSystemTrayControllerTest,
NotificationHiddenView_ModeHideSensitive);
FRIEND_TEST_ALL_PREFIXES(UnifiedSystemTrayControllerTest,
NotificationHiddenView_ModeProhibited);
// Modes of the lock screen notification.
enum class Mode { PROHIBITED, HIDE, SHOW, HIDE_SENSITIVE };

@ -107,7 +107,7 @@ TEST_F(NetworkDetailedNetworkViewPixelTest, Basics) {
// Show the detailed view.
system_tray->bubble()
->unified_system_tray_controller()
->ShowNetworkDetailedView(/*force=*/true);
->ShowNetworkDetailedView();
TrayDetailedView* detailed_view =
system_tray->bubble()
->quick_settings_view()

@ -208,7 +208,7 @@ void NetworkFeaturePodController::OnIconPressed() {
// well as when the network technology cannot be toggled, e.g. ethernet.
if (!was_enabled || !can_toggle) {
TrackDiveInUMA();
tray_controller_->ShowNetworkDetailedView(/*force=*/!can_toggle);
tray_controller_->ShowNetworkDetailedView();
}
}
@ -216,7 +216,7 @@ void NetworkFeaturePodController::OnLabelPressed() {
TrackDiveInUMA();
SetNetworkTypeEnabled(true);
tray_controller_->ShowNetworkDetailedView(/*force=*/true);
tray_controller_->ShowNetworkDetailedView();
}
void NetworkFeaturePodController::PropagateThemeChanged() {

@ -19,7 +19,6 @@
#include "ash/system/unified/unified_system_tray_bubble.h"
#include "ash/system/unified/unified_system_tray_controller.h"
#include "ash/system/unified/unified_system_tray_model.h"
#include "ash/system/unified/unified_system_tray_view.h"
#include "ash/test/ash_test_base.h"
#include "base/strings/stringprintf.h"
#include "base/test/metrics/histogram_tester.h"
@ -320,10 +319,6 @@ class NetworkFeaturePodControllerTest : public AshTestBase {
->unified_system_tray_controller();
}
UnifiedSystemTrayView* unified_view() {
return GetPrimaryUnifiedSystemTray()->bubble()->unified_view();
}
QuickSettingsView* quick_settings_view() {
return GetPrimaryUnifiedSystemTray()->bubble()->quick_settings_view();
}

@ -15,7 +15,6 @@
#include "ash/system/tray/tray_utils.h"
#include "ash/system/unified/unified_system_tray.h"
#include "ash/system/unified/unified_system_tray_bubble.h"
#include "ash/system/unified/unified_system_tray_view.h"
#include "base/functional/bind.h"
namespace ash {

@ -37,7 +37,7 @@ class UnifiedCalendarViewController : public DetailedViewController {
// Unowned, the object that instantiated us.
const raw_ptr<UnifiedSystemTrayController, ExperimentalAsh> tray_controller_;
// Owned by UnifiedSystemTrayView's detailed_view_container_.
// Owned by `QuickSettingsView`'s detailed_view_container_.
raw_ptr<CalendarView, DanglingUntriaged | ExperimentalAsh> view_ = nullptr;
};

@ -19,7 +19,6 @@
#include "ash/style/system_shadow.h"
#include "ash/system/tray/system_tray_notifier.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/unified/unified_system_tray_view.h"
#include "base/memory/raw_ptr.h"
#include "chromeos/constants/chromeos_features.h"
#include "third_party/skia/include/core/SkCanvas.h"

@ -122,11 +122,7 @@ constexpr int kTrayPopupInkDropCornerRadius = 2;
// Threshold to ignore update on the slider value.
constexpr float kAudioSliderIgnoreUpdateThreshold = 0.01;
// Duration for the collapse / expand animation in ms.
constexpr int kSystemMenuCollapseExpandAnimationDurationMs = 500;
constexpr auto kUnifiedMenuItemPadding = gfx::Insets::TLBR(0, 16, 16, 16);
constexpr auto kUnifiedSystemInfoViewPadding = gfx::Insets::TLBR(0, 16, 16, 16);
constexpr int kSliderChildrenViewSpacing = 8;
// Constants used in the QuickSettingsSlider of the `QuickSettingsView`.
@ -223,9 +219,6 @@ constexpr int kFeatureTileMaxRowsWhenMediaViewIsShowing = 3;
constexpr int kFeatureTileMinRows = 1;
constexpr int kFeatureTileHeight = 64;
// Height of the page indicator view.
constexpr int kPageIndicatorViewMaxHeight = 20;
// Constants used in system tray page transition animations.
constexpr double kCollapseThreshold = 0.3;

@ -401,8 +401,7 @@ TEST_F(TrayEventFilterTest, NotCloseTrayBubbleWhenTranscientChildActivated) {
auto* bubble = system_tray->bubble();
// Show the network detailed view.
bubble->unified_system_tray_controller()->ShowNetworkDetailedView(
/*force=*/true);
bubble->unified_system_tray_controller()->ShowNetworkDetailedView();
// Click on the info button in the network detailed view so that a transient
// bubble is opened.

@ -16,7 +16,7 @@ class FeatureTile;
// Base class for controllers of feature pod buttons.
// To add a new feature pod button, implement this class, and add to the list in
// UnifiedSystemTrayController::InitFeaturePods().
// UnifiedSystemTrayController::InitFeatureTiles().
class ASH_EXPORT FeaturePodControllerBase {
public:
virtual ~FeaturePodControllerBase() {}

@ -1,405 +0,0 @@
// 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/feature_pods_container_view.h"
#include "ash/public/cpp/pagination/pagination_controller.h"
#include "ash/public/cpp/pagination/pagination_model.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/unified/feature_pod_button.h"
#include "ash/system/unified/unified_system_tray_controller.h"
#include "base/ranges/algorithm.h"
namespace ash {
FeaturePodsContainerView::FeaturePodsContainerView(
UnifiedSystemTrayController* controller,
bool initially_expanded)
: controller_(controller),
pagination_model_(controller->model()->pagination_model()),
expanded_amount_(initially_expanded ? 1.0 : 0.0),
feature_pod_rows_(kUnifiedFeaturePodMaxRows) {
pagination_model_->AddObserver(this);
}
FeaturePodsContainerView::~FeaturePodsContainerView() {
DCHECK(pagination_model_);
pagination_model_->RemoveObserver(this);
}
void FeaturePodsContainerView::SetExpandedAmount(double expanded_amount) {
DCHECK(0.0 <= expanded_amount && expanded_amount <= 1.0);
if (expanded_amount_ == expanded_amount)
return;
expanded_amount_ = expanded_amount;
int visible_index = 0;
for (auto* view : children()) {
FeaturePodButton* button = static_cast<FeaturePodButton*>(view);
// When collapsing from page 1, buttons below the second row fade out
// while the rest move up into a single row for the collapsed state.
// When collapsing from page > 1, each row of buttons fades out one by one
// and once expanded_amount is less than kCollapseThreshold we begin to
// fade in the single row of buttons for the collapsed state.
if (expanded_amount_ > 0.0 && expanded_amount_ < kCollapseThreshold &&
pagination_model_->selected_page() > 0) {
button->SetExpandedAmount(1.0 - expanded_amount,
true /* fade_icon_button */);
} else if (visible_index > kUnifiedFeaturePodMaxItemsInCollapsed) {
int row =
(visible_index / kUnifiedFeaturePodItemsInRow) % feature_pod_rows_;
double button_expanded_amount =
expanded_amount
? std::min(1.0, expanded_amount +
(0.25 * (feature_pod_rows_ - row - 1)))
: expanded_amount;
button->SetExpandedAmount(button_expanded_amount,
true /* fade_icon_button */);
} else {
button->SetExpandedAmount(expanded_amount, false /* fade_icon_button */);
}
if (view->GetVisible())
visible_index++;
}
UpdateChildVisibility();
// We have to call Layout() explicitly here.
Layout();
}
int FeaturePodsContainerView::GetExpandedHeight() const {
const int visible_count = GetVisibleCount();
// floor(visible_count / kUnifiedFeaturePodItemsInRow)
int number_of_lines = (visible_count + kUnifiedFeaturePodItemsInRow - 1) /
kUnifiedFeaturePodItemsInRow;
number_of_lines = std::min(number_of_lines, feature_pod_rows_);
return kUnifiedFeaturePodBottomPadding +
(kUnifiedFeaturePodVerticalPadding + kUnifiedFeaturePodSize.height()) *
std::max(0, number_of_lines - 1) +
kUnifiedFeaturePodSize.height() + kUnifiedFeaturePodTopPadding;
}
int FeaturePodsContainerView::GetCollapsedHeight() const {
return 2 * kUnifiedFeaturePodCollapsedVerticalPadding +
kUnifiedFeaturePodCollapsedSize.height();
}
gfx::Size FeaturePodsContainerView::CalculatePreferredSize() const {
return gfx::Size(
kTrayMenuWidth,
static_cast<int>(GetCollapsedHeight() * (1.0 - expanded_amount_) +
GetExpandedHeight() * expanded_amount_));
}
void FeaturePodsContainerView::ChildVisibilityChanged(View* child) {
// ChildVisibilityChanged can change child visibility using
// SetVisibleByContainer() in UpdateChildVisibility(), so we have to prevent
// reentrancy.
if (changing_visibility_)
return;
// Visibility change is caused by the child's SetVisible(), so update actual
// visibility and propagate the container size change to the parent.
UpdateChildVisibility();
PreferredSizeChanged();
Layout();
SchedulePaint();
}
void FeaturePodsContainerView::ViewHierarchyChanged(
const views::ViewHierarchyChangedDetails& details) {
UpdateChildVisibility();
}
void FeaturePodsContainerView::Layout() {
UpdateCollapsedSidePadding();
CalculateIdealBoundsForFeaturePods();
for (size_t i = 0; i < visible_buttons_.view_size(); ++i) {
auto* button = visible_buttons_.view_at(i);
button->SetBoundsRect(visible_buttons_.ideal_bounds(i));
}
}
const char* FeaturePodsContainerView::GetClassName() const {
return "FeaturePodsContainerView";
}
void FeaturePodsContainerView::UpdateChildVisibility() {
DCHECK(!changing_visibility_);
changing_visibility_ = true;
int visible_count = 0;
for (auto* view : children()) {
auto* child = static_cast<FeaturePodButton*>(view);
bool visible = IsButtonVisible(child, visible_count);
child->SetVisibleByContainer(visible);
if (visible) {
if (!visible_buttons_.GetIndexOfView(child).has_value())
visible_buttons_.Add(child, visible_count);
++visible_count;
} else {
if (auto index = visible_buttons_.GetIndexOfView(child);
index.has_value()) {
visible_buttons_.Remove(index.value());
}
}
}
UpdateTotalPages();
changing_visibility_ = false;
}
bool FeaturePodsContainerView::IsButtonVisible(FeaturePodButton* button,
int index) {
return button->visible_preferred() &&
(expanded_amount_ > 0.0 ||
index < kUnifiedFeaturePodMaxItemsInCollapsed);
}
int FeaturePodsContainerView::GetVisibleCount() const {
return base::ranges::count_if(children(), [](const auto* v) {
return static_cast<const FeaturePodButton*>(v)->visible_preferred();
});
}
void FeaturePodsContainerView::EnsurePageWithButton(views::View* button) {
auto index = visible_buttons_.GetIndexOfView(button->parent());
if (!index.has_value())
return;
int tiles_per_page = GetTilesPerPage();
size_t first_index = pagination_model_->selected_page() * tiles_per_page;
size_t last_index =
((pagination_model_->selected_page() + 1) * tiles_per_page) - 1;
if (index.value() < first_index || index.value() > last_index) {
int page = ((index.value() + 1) / tiles_per_page) +
((index.value() + 1) % tiles_per_page ? 1 : 0) - 1;
pagination_model_->SelectPage(page, true /*animate*/);
}
}
void FeaturePodsContainerView::SelectedPageChanged(int old_selected,
int new_selected) {
InvalidateLayout();
PaginationModelObserver::SelectedPageChanged(old_selected, new_selected);
}
gfx::Point FeaturePodsContainerView::GetButtonPosition(
int visible_index) const {
int row = visible_index / kUnifiedFeaturePodItemsInRow;
int column = visible_index % kUnifiedFeaturePodItemsInRow;
int x = kUnifiedFeaturePodHorizontalSidePadding +
(kUnifiedFeaturePodSize.width() +
kUnifiedFeaturePodHorizontalMiddlePadding) *
column;
int y = kUnifiedFeaturePodTopPadding + (kUnifiedFeaturePodSize.height() +
kUnifiedFeaturePodVerticalPadding) *
row;
// Only feature pods visible in the collapsed state (i.e. the first 5 pods)
// move during expansion/collapse. Otherwise, the button position will always
// be constant.
if (expanded_amount_ == 1.0 ||
visible_index > kUnifiedFeaturePodMaxItemsInCollapsed ||
(pagination_model_->selected_page() > 0 &&
expanded_amount_ >= kCollapseThreshold)) {
return gfx::Point(x, y);
}
int collapsed_x =
collapsed_side_padding_ + (kUnifiedFeaturePodCollapsedSize.width() +
kUnifiedFeaturePodCollapsedHorizontalPadding) *
visible_index;
int collapsed_y = kUnifiedFeaturePodCollapsedVerticalPadding;
// When fully collapsed or collapsing from a different page to the first
// page, just return the collapsed position.
if (expanded_amount_ == 0.0 || (expanded_amount_ < kCollapseThreshold &&
pagination_model_->selected_page() > 0))
return gfx::Point(collapsed_x, collapsed_y);
// Button width is different between expanded and collapsed states.
// During the transition, expanded width is used, so it should be adjusted.
collapsed_x -= (kUnifiedFeaturePodSize.width() -
kUnifiedFeaturePodCollapsedSize.width()) /
2;
return gfx::Point(
x * expanded_amount_ + collapsed_x * (1.0 - expanded_amount_),
y * expanded_amount_ + collapsed_y * (1.0 - expanded_amount_));
}
int FeaturePodsContainerView::CalculateRowsFromHeight(int height) {
int available_height =
height - kUnifiedFeaturePodBottomPadding - kUnifiedFeaturePodTopPadding;
int row_height =
kUnifiedFeaturePodSize.height() + kUnifiedFeaturePodVerticalPadding;
// Only use the max number of rows when there is enough space
// to show the fully expanded message center and quick settings.
if (available_height > (kUnifiedFeaturePodMaxRows * row_height) &&
available_height - (kUnifiedFeaturePodMaxRows * row_height) >
kMessageCenterCollapseThreshold) {
return kUnifiedFeaturePodMaxRows;
}
// Use 1 less than the max number of rows when there is enough
// space to show the message center in the collapsed state along
// with the expanded quick settings.
int feature_pod_rows = kUnifiedFeaturePodMaxRows - 1;
if (available_height > (feature_pod_rows * row_height) &&
available_height - (feature_pod_rows * row_height) >
kStackedNotificationBarHeight) {
return feature_pod_rows;
}
return kUnifiedFeaturePodMinRows;
}
void FeaturePodsContainerView::SetMaxHeight(int max_height) {
int feature_pod_rows = CalculateRowsFromHeight(max_height);
if (feature_pod_rows_ != feature_pod_rows) {
feature_pod_rows_ = feature_pod_rows;
UpdateTotalPages();
}
}
void FeaturePodsContainerView::UpdateCollapsedSidePadding() {
const int visible_count =
std::min(GetVisibleCount(), kUnifiedFeaturePodMaxItemsInCollapsed);
int contents_width =
visible_count * kUnifiedFeaturePodCollapsedSize.width() +
(visible_count - 1) * kUnifiedFeaturePodCollapsedHorizontalPadding;
collapsed_side_padding_ = (kTrayMenuWidth - contents_width) / 2;
DCHECK(collapsed_side_padding_ > 0);
}
void FeaturePodsContainerView::AddFeaturePodButton(FeaturePodButton* button) {
size_t view_size = visible_buttons_.view_size();
if (IsButtonVisible(button, view_size)) {
visible_buttons_.Add(button, view_size);
}
AddChildView(button);
UpdateTotalPages();
}
const gfx::Vector2d FeaturePodsContainerView::CalculateTransitionOffset(
int page_of_view) const {
gfx::Size grid_size = CalculatePreferredSize();
// If there is a transition, calculates offset for current and target page.
const int current_page = pagination_model_->selected_page();
const PaginationModel::Transition& transition =
pagination_model_->transition();
const bool is_valid =
pagination_model_->is_valid_page(transition.target_page);
// Transition to previous page means negative offset.
const int direction = transition.target_page > current_page ? -1 : 1;
int x_offset = 0;
int y_offset = 0;
// Page size including padding pixels. A tile.x + page_width means the same
// tile slot in the next page.
const int page_width = grid_size.width() + kUnifiedFeaturePodsPageSpacing;
if (page_of_view < current_page)
x_offset = -page_width;
else if (page_of_view > current_page)
x_offset = page_width;
if (is_valid) {
if (page_of_view == current_page ||
page_of_view == transition.target_page) {
x_offset += transition.progress * page_width * direction;
}
}
return gfx::Vector2d(x_offset, y_offset);
}
void FeaturePodsContainerView::CalculateIdealBoundsForFeaturePods() {
for (size_t i = 0; i < visible_buttons_.view_size(); ++i) {
gfx::Rect tile_bounds;
gfx::Size child_size;
// When we are on the first page we calculate bounds for an expanded tray
// when expanded_amount is greater than zero. However, when not on the first
// page, we only calculate bounds for an expanded tray until expanded_amount
// is above kCollapseThreshold. Below kCollapseThreshold we return collapsed
// bounds.
if ((expanded_amount_ > 0.0 && pagination_model_->selected_page() == 0) ||
expanded_amount_ >= kCollapseThreshold) {
child_size = kUnifiedFeaturePodSize;
// Flexibly give more height if the child view doesn't fit into the
// default height, so that label texts won't be broken up in the middle.
child_size.set_height(std::max(
child_size.height(),
visible_buttons_.view_at(i)->GetHeightForWidth(child_size.height())));
tile_bounds =
gfx::Rect(GetButtonPosition(i % GetTilesPerPage()), child_size);
// TODO(amehfooz): refactor this logic so that the ideal_bounds are set
// once when the transition starts and the actual feature pod bounds are
// interpolated using the ideal_bounds as the transition progresses.
tile_bounds.Offset(CalculateTransitionOffset(i / GetTilesPerPage()));
} else {
child_size = kUnifiedFeaturePodCollapsedSize;
tile_bounds = gfx::Rect(GetButtonPosition(i), child_size);
}
visible_buttons_.set_ideal_bounds(i, tile_bounds);
}
}
int FeaturePodsContainerView::GetTilesPerPage() const {
return kUnifiedFeaturePodItemsInRow * feature_pod_rows_;
}
void FeaturePodsContainerView::UpdateTotalPages() {
int total_pages = 0;
size_t total_visible = visible_buttons_.view_size();
int tiles_per_page = GetTilesPerPage();
if (!visible_buttons_.view_size() || !tiles_per_page) {
total_pages = 0;
} else {
total_pages = (total_visible / tiles_per_page) +
(total_visible % tiles_per_page ? 1 : 0);
}
pagination_model_->SetTotalPages(total_pages);
}
void FeaturePodsContainerView::TransitionChanged() {
const PaginationModel::Transition& transition =
pagination_model_->transition();
if (pagination_model_->is_valid_page(transition.target_page))
Layout();
}
void FeaturePodsContainerView::OnGestureEvent(ui::GestureEvent* event) {
if (controller_->pagination_controller()->OnGestureEvent(*event,
GetContentsBounds()))
event->SetHandled();
}
void FeaturePodsContainerView::OnScrollEvent(ui::ScrollEvent* event) {
controller_->pagination_controller()->OnScroll(
gfx::Vector2d(event->x_offset(), event->y_offset()), event->type());
event->SetHandled();
}
bool FeaturePodsContainerView::OnMouseWheel(const ui::MouseWheelEvent& event) {
return controller_->pagination_controller()->OnScroll(event.offset(),
event.type());
}
} // namespace ash

@ -1,139 +0,0 @@
// 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.
#ifndef ASH_SYSTEM_UNIFIED_FEATURE_PODS_CONTAINER_VIEW_H_
#define ASH_SYSTEM_UNIFIED_FEATURE_PODS_CONTAINER_VIEW_H_
#include "ash/ash_export.h"
#include "ash/public/cpp/pagination/pagination_model_observer.h"
#include "base/memory/raw_ptr.h"
#include "ui/views/view.h"
#include "ui/views/view_model.h"
namespace ash {
class FeaturePodButton;
class PaginationModel;
class UnifiedSystemTrayController;
// Container of feature pods buttons in the middle of UnifiedSystemTrayView.
// The container has number of buttons placed in 3x3 plane at regular distances.
// FeaturePodButtons implements these individual buttons.
// The container also implements collapsed state where the top 5 buttons are
// horizontally placed and others are hidden.
class ASH_EXPORT FeaturePodsContainerView : public views::View,
public PaginationModelObserver {
public:
FeaturePodsContainerView(UnifiedSystemTrayController* controller,
bool initially_expanded);
FeaturePodsContainerView(const FeaturePodsContainerView&) = delete;
FeaturePodsContainerView& operator=(const FeaturePodsContainerView&) = delete;
~FeaturePodsContainerView() override;
// Add a FeaturePodButton as a child view and if it's visible add it to the
// view structure and update the pagination model.
void AddFeaturePodButton(FeaturePodButton* button);
// Change the expanded state. 0.0 if collapsed, and 1.0 if expanded.
// Otherwise, it shows intermediate state. If collapsed, all the buttons are
// horizontally placed.
void SetExpandedAmount(double expanded_amount);
// Set the number of rows of feature pods based on the max height the
// container can have.
void SetMaxHeight(int max_height);
// Get height of the view when |expanded_amount| is set to 1.0.
int GetExpandedHeight() const;
// Get the height of the view when |expanded_amount| is set to 0.0.
int GetCollapsedHeight() const;
// Returns the number of children that prefer to be visible.
int GetVisibleCount() const;
// Make sure button is visible by switching page if needed.
void EnsurePageWithButton(views::View* button);
// PaginationModelObserver
void SelectedPageChanged(int old_selected, int new_selected) override;
// views::View:
gfx::Size CalculatePreferredSize() const override;
void ChildVisibilityChanged(View* child) override;
void ViewHierarchyChanged(
const views::ViewHierarchyChangedDetails& details) override;
void Layout() override;
void OnGestureEvent(ui::GestureEvent* event) override;
void OnScrollEvent(ui::ScrollEvent* event) override;
bool OnMouseWheel(const ui::MouseWheelEvent& event) override;
const char* GetClassName() const override;
int row_count() const { return feature_pod_rows_; }
private:
friend class FeaturePodsContainerViewTest;
// Calculate the current position of the button from |visible_index| and
// |expanded_amount_|.
gfx::Point GetButtonPosition(int visible_index) const;
void UpdateChildVisibility();
// Update |collapsed_state_padding_|.
void UpdateCollapsedSidePadding();
// Calculates the ideal bounds for all feature pods.
void CalculateIdealBoundsForFeaturePods();
// Calculate the number of feature pod rows based on available height.
int CalculateRowsFromHeight(int height);
// Calculates the offset for |page_of_view| based on current page and
// transition target page.
const gfx::Vector2d CalculateTransitionOffset(int page_of_view) const;
// Returns true if button at provided index is visible.
bool IsButtonVisible(FeaturePodButton* button, int index);
// Returns the number of tiles per page.
int GetTilesPerPage() const;
// Updates page splits for feature pod buttons.
void UpdateTotalPages();
// PaginationModelObserver:
void TransitionChanged() override;
const raw_ptr<UnifiedSystemTrayController,
DanglingUntriaged | ExperimentalAsh>
controller_;
// Owned by UnifiedSystemTrayModel.
const raw_ptr<PaginationModel, ExperimentalAsh> pagination_model_;
// The last |expanded_amount| passed to SetExpandedAmount().
double expanded_amount_;
// Number of rows of feature pods to display. Updated based on the available
// max height for FeaturePodsContainer.
int feature_pod_rows_ = 0;
// Horizontal side padding in dip for collapsed state.
int collapsed_side_padding_ = 0;
// Used for preventing reentrancy issue in ChildVisibilityChanged. Should be
// always false if FeaturePodsContainerView is not in the call stack.
bool changing_visibility_ = false;
// A view model that contains all visible feature pod buttons.
// Used to calculate required number of pages.
views::ViewModelT<FeaturePodButton> visible_buttons_;
};
} // namespace ash
#endif // ASH_SYSTEM_UNIFIED_FEATURE_PODS_CONTAINER_VIEW_H_

@ -1,463 +0,0 @@
// 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/feature_pods_container_view.h"
#include "ash/constants/quick_settings_catalogs.h"
#include "ash/public/cpp/pagination/pagination_controller.h"
#include "ash/public/cpp/pagination/pagination_model.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/unified/feature_pod_button.h"
#include "ash/system/unified/feature_pod_controller_base.h"
#include "ash/system/unified/unified_system_tray_controller.h"
#include "ash/system/unified/unified_system_tray_model.h"
#include "ash/test/ash_test_base.h"
#include "base/memory/scoped_refptr.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/views/test/views_test_utils.h"
#include "ui/views/view_observer.h"
namespace ash {
class FeaturePodsContainerViewTest : public NoSessionAshTestBase,
public FeaturePodControllerBase,
public views::ViewObserver {
public:
FeaturePodsContainerViewTest() = default;
FeaturePodsContainerViewTest(const FeaturePodsContainerViewTest&) = delete;
FeaturePodsContainerViewTest& operator=(const FeaturePodsContainerViewTest&) =
delete;
~FeaturePodsContainerViewTest() override = default;
// AshTestBase:
void SetUp() override {
AshTestBase::SetUp();
model_ = base::MakeRefCounted<UnifiedSystemTrayModel>(nullptr);
controller_ = std::make_unique<UnifiedSystemTrayController>(model_.get());
container_ = std::make_unique<FeaturePodsContainerView>(
controller_.get(), true /* initially_expanded */);
container_->AddObserver(this);
preferred_size_changed_count_ = 0;
}
void TearDown() override {
controller_.reset();
container_.reset();
model_.reset();
NoSessionAshTestBase::TearDown();
}
// FeaturePodControllerBase:
std::unique_ptr<FeatureTile> CreateTile(bool compact) override {
return nullptr;
}
void OnIconPressed() override {}
QsFeatureCatalogName GetCatalogName() override {
return QsFeatureCatalogName::kUnknown;
}
// views::ViewObserver:
void OnViewPreferredSizeChanged(views::View* observed_view) override {
++preferred_size_changed_count_;
}
protected:
void AddButton(FeaturePodButton* button) {
buttons_.push_back(button);
container()->AddChildView(button);
}
void AddButtons(int count) {
for (int i = 0; i < count; ++i)
AddButton(new FeaturePodButton(this));
container()->SetBoundsRect(gfx::Rect(container_->GetPreferredSize()));
views::test::RunScheduledLayout(container());
}
FeaturePodsContainerView* container() { return container_.get(); }
PaginationModel* pagination_model() { return model_->pagination_model(); }
UnifiedSystemTrayController* controller() { return controller_.get(); }
int preferred_size_changed_count() const {
return preferred_size_changed_count_;
}
std::vector<FeaturePodButton*> buttons_;
private:
std::unique_ptr<FeaturePodsContainerView> container_;
scoped_refptr<UnifiedSystemTrayModel> model_;
std::unique_ptr<UnifiedSystemTrayController> controller_;
int preferred_size_changed_count_ = 0;
};
TEST_F(FeaturePodsContainerViewTest, ExpandedAndCollapsed) {
const int kNumberOfAddedButtons = kUnifiedFeaturePodItemsInRow * 3;
EXPECT_LT(kUnifiedFeaturePodMaxItemsInCollapsed, kNumberOfAddedButtons);
AddButtons(kNumberOfAddedButtons);
// In expanded state, buttons are laid out in plane.
EXPECT_LT(buttons_[0]->x(), buttons_[1]->x());
EXPECT_EQ(buttons_[0]->y(), buttons_[1]->y());
// If the row exceeds kUnifiedFeaturePodItemsInRow, the next button is placed
// right under the first button.
EXPECT_EQ(buttons_[0]->x(), buttons_[kUnifiedFeaturePodItemsInRow]->x());
EXPECT_LT(buttons_[0]->y(), buttons_[kUnifiedFeaturePodItemsInRow]->y());
// All buttons are visible.
for (auto* button : buttons_)
EXPECT_TRUE(button->GetVisible());
container()->SetExpandedAmount(0.0);
// In collapsed state, all buttons are laid out horizontally.
for (int i = 1; i < kUnifiedFeaturePodMaxItemsInCollapsed; ++i)
EXPECT_EQ(buttons_[0]->y(), buttons_[i]->y());
// Buttons exceed kUnifiedFeaturePodMaxItemsInCollapsed are invisible.
for (int i = 0; i < kNumberOfAddedButtons; ++i) {
EXPECT_EQ(i < kUnifiedFeaturePodMaxItemsInCollapsed,
buttons_[i]->GetVisible());
}
}
TEST_F(FeaturePodsContainerViewTest, HiddenButtonRemainsHidden) {
AddButtons(kUnifiedFeaturePodMaxItemsInCollapsed);
// The button is invisible in expanded state.
buttons_.front()->SetVisible(false);
container()->SetExpandedAmount(0.0);
EXPECT_FALSE(buttons_.front()->GetVisible());
container()->SetExpandedAmount(1.0);
EXPECT_FALSE(buttons_.front()->GetVisible());
}
TEST_F(FeaturePodsContainerViewTest, BecomeVisibleInCollapsed) {
AddButtons(kUnifiedFeaturePodMaxItemsInCollapsed);
// The button is invisible in expanded state.
buttons_.back()->SetVisible(false);
container()->SetExpandedAmount(0.0);
// The button becomes visible in collapsed state.
buttons_.back()->SetVisible(true);
// As the container still has remaining space, the button will be visible.
EXPECT_TRUE(buttons_.back()->GetVisible());
}
TEST_F(FeaturePodsContainerViewTest, StillHiddenInCollapsed) {
AddButtons(kUnifiedFeaturePodMaxItemsInCollapsed + 1);
// The button is invisible in expanded state.
buttons_.back()->SetVisible(false);
container()->SetExpandedAmount(0.0);
// The button becomes visible in collapsed state.
buttons_.back()->SetVisible(true);
// As the container doesn't have remaining space, the button won't be visible.
EXPECT_FALSE(buttons_.back()->GetVisible());
container()->SetExpandedAmount(1.0);
// The button becomes visible in expanded state.
EXPECT_TRUE(buttons_.back()->GetVisible());
}
TEST_F(FeaturePodsContainerViewTest, DifferentButtonBecomeVisibleInCollapsed) {
AddButtons(kUnifiedFeaturePodMaxItemsInCollapsed + 1);
container()->SetExpandedAmount(0.0);
// The last button is not visible as it doesn't have enough space.
EXPECT_FALSE(buttons_.back()->GetVisible());
// The first button becomes invisible.
buttons_.front()->SetVisible(false);
// The last button now has the space for it.
EXPECT_TRUE(buttons_.back()->GetVisible());
}
TEST_F(FeaturePodsContainerViewTest, SizeChangeByExpanding) {
// SetExpandedAmount() should not trigger PreferredSizeChanged().
AddButtons(kUnifiedFeaturePodItemsInRow * 3 - 1);
EXPECT_EQ(0, preferred_size_changed_count());
container()->SetExpandedAmount(0.0);
container()->SetExpandedAmount(0.5);
container()->SetExpandedAmount(1.0);
EXPECT_EQ(0, preferred_size_changed_count());
}
TEST_F(FeaturePodsContainerViewTest, SizeChangeByVisibility) {
// Visibility change should trigger PreferredSizeChanged().
AddButtons(kUnifiedFeaturePodItemsInRow * 2 + 1);
EXPECT_EQ(0, preferred_size_changed_count());
// The first button becomes invisible.
buttons_.front()->SetVisible(false);
EXPECT_EQ(1, preferred_size_changed_count());
// The first button becomes visible.
buttons_.front()->SetVisible(true);
EXPECT_EQ(2, preferred_size_changed_count());
}
TEST_F(FeaturePodsContainerViewTest, NumberOfPagesChanged) {
const int kNumberOfPages = 8;
AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodMaxRows *
kNumberOfPages);
// Adding buttons to fill kNumberOfPages should cause the the same number of
// pages to be created.
EXPECT_EQ(kNumberOfPages, pagination_model()->total_pages());
// Adding an additional button causes a new page to be added.
AddButtons(1);
EXPECT_EQ(pagination_model()->total_pages(), kNumberOfPages + 1);
}
TEST_F(FeaturePodsContainerViewTest, PaginationTransition) {
const int kNumberOfPages = 8;
AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodMaxRows *
kNumberOfPages);
// Position of a button should slide to the left during a page
// transition to the next page.
gfx::Rect current_bounds;
gfx::Rect initial_bounds = buttons_[0]->bounds();
gfx::Rect previous_bounds = initial_bounds;
PaginationModel::Transition transition(
pagination_model()->selected_page() + 1, 0);
for (double i = 0.1; i <= 1.0; i += 0.1) {
transition.progress = i;
pagination_model()->SetTransition(transition);
current_bounds = buttons_[0]->bounds();
EXPECT_LT(current_bounds.x(), previous_bounds.x());
EXPECT_EQ(current_bounds.y(), previous_bounds.y());
previous_bounds = current_bounds;
}
// Button Position after page switch should move to the left by a page offset.
int page_offset = container()->CalculatePreferredSize().width() +
kUnifiedFeaturePodsPageSpacing;
gfx::Rect final_bounds =
gfx::Rect(initial_bounds.x() - page_offset, initial_bounds.y(),
initial_bounds.width(), initial_bounds.height());
pagination_model()->SelectPage(1, false);
views::test::RunScheduledLayout(container());
EXPECT_EQ(final_bounds, buttons_[0]->bounds());
}
TEST_F(FeaturePodsContainerViewTest, PaginationDynamicRows) {
const int kNumberOfFeaturePods = kUnifiedFeaturePodItemsInRow * 3;
const int padding =
kUnifiedFeaturePodTopPadding + kUnifiedFeaturePodBottomPadding;
int row_height =
kUnifiedFeaturePodSize.height() + kUnifiedFeaturePodVerticalPadding;
int min_height_for_three_rows = kUnifiedFeaturePodMaxRows * row_height +
padding + kMessageCenterCollapseThreshold;
AddButtons(kNumberOfFeaturePods);
// Expect 1 row of feature pods even if there is 0 height.
container()->SetMaxHeight(0);
int expected_number_of_pages =
kNumberOfFeaturePods / kUnifiedFeaturePodItemsInRow;
if (kNumberOfFeaturePods % kUnifiedFeaturePodItemsInRow)
expected_number_of_pages += 1;
EXPECT_EQ(expected_number_of_pages, pagination_model()->total_pages());
// Expect 2 rows of feature pods when there is enough height to display them
// but less than enough to display 3 rows.
container()->SetMaxHeight(min_height_for_three_rows - 1);
expected_number_of_pages =
kNumberOfFeaturePods / (2 * kUnifiedFeaturePodItemsInRow);
if (kNumberOfFeaturePods % (2 * kUnifiedFeaturePodItemsInRow))
expected_number_of_pages += 1;
EXPECT_EQ(expected_number_of_pages, pagination_model()->total_pages());
// Expect 3 rows of feature pods at max even when the max height is very
// large.
container()->SetMaxHeight(min_height_for_three_rows + 1);
expected_number_of_pages =
kNumberOfFeaturePods / (3 * kUnifiedFeaturePodItemsInRow);
if (kNumberOfFeaturePods % (3 * kUnifiedFeaturePodItemsInRow))
expected_number_of_pages += 1;
EXPECT_EQ(expected_number_of_pages, pagination_model()->total_pages());
}
TEST_F(FeaturePodsContainerViewTest, PaginationGestureHandling) {
const int kNumberOfPages = 8;
AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodMaxRows *
kNumberOfPages);
gfx::Point container_origin = container()->GetBoundsInScreen().origin();
ui::GestureEvent swipe_left_begin(
container_origin.x(), container_origin.y(), 0, base::TimeTicks(),
ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, -1, 0));
ui::GestureEvent swipe_left_update(
container_origin.x(), container_origin.y(), 0, base::TimeTicks(),
ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, -1000, 0));
ui::GestureEvent swipe_right_begin(
container_origin.x(), container_origin.y(), 0, base::TimeTicks(),
ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, 1, 0));
ui::GestureEvent swipe_right_update(
container_origin.x(), container_origin.y(), 0, base::TimeTicks(),
ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 1000, 0));
ui::GestureEvent swipe_end(container_origin.x(), container_origin.y(), 0,
base::TimeTicks(),
ui::GestureEventDetails(ui::ET_GESTURE_END));
int previous_page = pagination_model()->selected_page();
// Swipe left takes to next page
for (int i = 0; i < kNumberOfPages - 1; i++) {
// Simulate swipe left
container()->OnGestureEvent(&swipe_left_begin);
container()->OnGestureEvent(&swipe_left_update);
container()->OnGestureEvent(&swipe_end);
int current_page = pagination_model()->selected_page();
// Expect next page
EXPECT_EQ(previous_page + 1, current_page);
previous_page = current_page;
}
// Swipe left on last page does nothing
container()->OnGestureEvent(&swipe_left_begin);
container()->OnGestureEvent(&swipe_left_update);
container()->OnGestureEvent(&swipe_end);
EXPECT_EQ(previous_page, pagination_model()->selected_page());
// Swipe right takes to previous page
for (int i = 0; i < kNumberOfPages - 1; i++) {
// Simulate swipe right
container()->OnGestureEvent(&swipe_right_begin);
container()->OnGestureEvent(&swipe_right_update);
container()->OnGestureEvent(&swipe_end);
int current_page = pagination_model()->selected_page();
// Expect previous page
EXPECT_EQ(previous_page - 1, current_page);
previous_page = current_page;
}
// Swipe right on first page does nothing
container()->OnGestureEvent(&swipe_right_begin);
container()->OnGestureEvent(&swipe_right_update);
container()->OnGestureEvent(&swipe_end);
EXPECT_EQ(previous_page, pagination_model()->selected_page());
}
TEST_F(FeaturePodsContainerViewTest, PaginationScrollHandling) {
const int kNumberOfPages = 8;
const int num_fingers = 2;
AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodMaxRows *
kNumberOfPages);
EXPECT_EQ(kNumberOfPages, pagination_model()->total_pages());
gfx::Point container_origin = container()->GetBoundsInScreen().origin();
ui::ScrollEvent fling_up_start(ui::ET_SCROLL_FLING_START, container_origin,
base::TimeTicks(), 0, 0, 100, 0, 10,
num_fingers);
ui::ScrollEvent fling_down_start(ui::ET_SCROLL_FLING_START, container_origin,
base::TimeTicks(), 0, 0, -100, 0, 10,
num_fingers);
ui::ScrollEvent fling_cancel(ui::ET_SCROLL_FLING_CANCEL, container_origin,
base::TimeTicks(), 0, 0, 0, 0, 0, num_fingers);
int previous_page = pagination_model()->selected_page();
// Scroll down takes to next page
for (int i = 0; i < kNumberOfPages - 1; i++) {
// Simulate Scroll left
container()->OnScrollEvent(&fling_down_start);
container()->OnScrollEvent(&fling_cancel);
pagination_model()->FinishAnimation();
int current_page = pagination_model()->selected_page();
// Expect next page
EXPECT_EQ(previous_page + 1, current_page);
previous_page = current_page;
}
// Scroll up takes to previous page
for (int i = 0; i < kNumberOfPages - 1; i++) {
// Simulate Scroll up
container()->OnScrollEvent(&fling_up_start);
container()->OnScrollEvent(&fling_cancel);
pagination_model()->FinishAnimation();
int current_page = pagination_model()->selected_page();
// Expect previous page
EXPECT_EQ(previous_page - 1, current_page);
previous_page = current_page;
}
}
TEST_F(FeaturePodsContainerViewTest, PaginationMouseWheelHandling) {
const int kNumberOfPages = 8;
AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodMaxRows *
kNumberOfPages);
gfx::Point container_origin = container()->GetBoundsInScreen().origin();
ui::MouseWheelEvent wheel_up(gfx::Vector2d(0, 1000), container_origin,
container_origin, base::TimeTicks(), 0, 0);
ui::MouseWheelEvent wheel_down(gfx::Vector2d(0, -1000), container_origin,
container_origin, base::TimeTicks(), 0, 0);
int previous_page = pagination_model()->selected_page();
// Mouse wheel down takes to next page
for (int i = 0; i < kNumberOfPages - 1; i++) {
// Simulate mouse wheel down
container()->OnMouseWheel(wheel_down);
pagination_model()->FinishAnimation();
int current_page = pagination_model()->selected_page();
// Expect next page
EXPECT_EQ(previous_page + 1, current_page);
previous_page = current_page;
}
// Mouse wheel up takes to previous page
for (int i = 0; i < kNumberOfPages - 1; i++) {
// Simulate mouse wheel up
container()->OnMouseWheel(wheel_up);
pagination_model()->FinishAnimation();
int current_page = pagination_model()->selected_page();
// Expect previous page
EXPECT_EQ(previous_page - 1, current_page);
previous_page = current_page;
}
}
TEST_F(FeaturePodsContainerViewTest, NonTogglableButton) {
// Add one togglable and one non-tobblable button.
AddButton(new FeaturePodButton(this, /*is_togglable=*/false));
AddButtons(1);
// Non-togglable buttons should be labelled as a regular button for
// accessibility and vice versa.
ui::AXNodeData ax_node_data;
buttons_[0]->icon_button()->GetAccessibleNodeData(&ax_node_data);
EXPECT_EQ(ax_node_data.role, ax::mojom::Role::kButton);
buttons_[1]->icon_button()->GetAccessibleNodeData(&ax_node_data);
EXPECT_EQ(ax_node_data.role, ax::mojom::Role::kToggleButton);
}
} // namespace ash

@ -1,103 +0,0 @@
// 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/notification_hidden_view.h"
#include "ash/bubble/bubble_constants.h"
#include "ash/login/login_screen_controller.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/style/pill_button.h"
#include "ash/system/message_center/ash_message_center_lock_screen_controller.h"
#include "ash/system/message_center/message_center_constants.h"
#include "ash/system/tray/tray_constants.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/message_center/lock_screen/lock_screen_controller.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/message_center_impl.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h"
namespace ash {
namespace {
void ShowLockScreenNotificationSettings() {
ash::Shell::Get()
->login_screen_controller()
->ShowLockScreenNotificationSettings();
}
SkColor GetBackgroundColor() {
return AshColorProvider::Get()->GetControlsLayerColor(
AshColorProvider::ControlsLayerType::kControlBackgroundColorInactive);
}
} // namespace
NotificationHiddenView::NotificationHiddenView()
: container_(AddChildView(std::make_unique<views::View>())),
label_(container_->AddChildView(std::make_unique<views::Label>())) {
label_->SetAutoColorReadabilityEnabled(false);
label_->SetText(
l10n_util::GetStringUTF16(IDS_ASH_MESSAGE_CENTER_LOCKSCREEN_UNIFIED));
label_->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
label_->SetLineHeight(kUnifiedNotificationHiddenLineHeight);
label_->SetBorder(
views::CreateEmptyBorder(kUnifiedNotificationHiddenPadding));
container_->SetBackground(views::CreateRoundedRectBackground(
GetBackgroundColor(), kBubbleCornerRadius));
auto* layout =
container_->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal));
layout->SetFlexForView(label_, 1);
// Shows the "Change" button, unless the locks screen notification is
// prohibited by policy or flag.
if (AshMessageCenterLockScreenController::IsAllowed()) {
change_button_ = container_->AddChildView(std::make_unique<PillButton>(
base::BindRepeating(&NotificationHiddenView::ChangeButtonPressed,
base::Unretained(this)),
l10n_util::GetStringUTF16(IDS_ASH_MESSAGE_CENTER_LOCKSCREEN_CHANGE),
PillButton::Type::kDefaultWithoutIcon, /*icon=*/nullptr,
kNotificationPillButtonHorizontalSpacing));
change_button_->SetTooltipText(l10n_util::GetStringUTF16(
IDS_ASH_MESSAGE_CENTER_LOCKSCREEN_CHANGE_TOOLTIP));
}
SetBorder(views::CreateEmptyBorder(kUnifiedNotificationCenterSpacing));
SetLayoutManager(std::make_unique<views::FillLayout>());
}
const char* NotificationHiddenView::GetClassName() const {
return "NotificationHiddenView";
}
void NotificationHiddenView::OnThemeChanged() {
views::View::OnThemeChanged();
label_->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kTextColorPrimary));
container_->background()->SetNativeControlColor(GetBackgroundColor());
}
void NotificationHiddenView::ChangeButtonPressed() {
// TODO(yoshiki): Refactor LockScreenController and remove the static cast.
// TODO(yoshiki): Show the setting after unlocking.
static_cast<message_center::MessageCenterImpl*>(
message_center::MessageCenter::Get())
->lock_screen_controller()
->DismissLockScreenThenExecute(
base::BindOnce(&ShowLockScreenNotificationSettings),
base::DoNothing(), IDS_ASH_MESSAGE_CENTER_UNLOCK_TO_CHANGE_SETTING);
}
} // namespace ash

@ -1,46 +0,0 @@
// 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.
#ifndef ASH_SYSTEM_UNIFIED_NOTIFICATION_HIDDEN_VIEW_H_
#define ASH_SYSTEM_UNIFIED_NOTIFICATION_HIDDEN_VIEW_H_
#include "base/memory/raw_ptr.h"
#include "ui/views/view.h"
namespace views {
class Button;
class Label;
}
namespace ash {
// A view to show the message that notifications are hidden on the lock screen
// by the setting or the flag. This may show the button to encourage the user
// to change the lock screen notification setting if the condition permits.
class NotificationHiddenView : public views::View {
public:
NotificationHiddenView();
NotificationHiddenView(const NotificationHiddenView&) = delete;
NotificationHiddenView& operator=(const NotificationHiddenView&) = delete;
~NotificationHiddenView() override = default;
// views::View:
const char* GetClassName() const override;
void OnThemeChanged() override;
views::Button* change_button_for_testing() { return change_button_; }
private:
void ChangeButtonPressed();
const raw_ptr<views::View, ExperimentalAsh> container_;
const raw_ptr<views::Label, ExperimentalAsh> label_;
raw_ptr<views::Button, ExperimentalAsh> change_button_ = nullptr;
};
} // namespace ash
#endif // ASH_SYSTEM_UNIFIED_NOTIFICATION_HIDDEN_VIEW_H_

@ -1,221 +0,0 @@
// Copyright 2019 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/page_indicator_view.h"
#include <algorithm>
#include <memory>
#include <utility>
#include "ash/app_list/app_list_metrics.h"
#include "ash/public/cpp/pagination/pagination_model.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/style/color_util.h"
#include "ash/style/style_util.h"
#include "ash/system/tray/tray_popup_utils.h"
#include "ash/system/unified/unified_system_tray_controller.h"
#include "base/functional/bind.h"
#include "base/i18n/number_formatting.h"
#include "base/metrics/histogram_macros.h"
#include "third_party/skia/include/core/SkPath.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/animation/throb_animation.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/skia_conversions.h"
#include "ui/views/animation/ink_drop.h"
#include "ui/views/background.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/highlight_path_generator.h"
#include "ui/views/layout/box_layout.h"
namespace ash {
namespace {
constexpr int kUnifiedPageIndicatorButtonRadius = 3;
constexpr int kInkDropRadius = 3 * kUnifiedPageIndicatorButtonRadius;
} // namespace
// Button internally used in PageIndicatorView. Each button
// stores a page number which it switches to if pressed.
class PageIndicatorView::PageIndicatorButton : public views::Button {
public:
PageIndicatorButton(UnifiedSystemTrayController* controller, int page)
: views::Button(base::BindRepeating(
&UnifiedSystemTrayController::HandlePageSwitchAction,
base::Unretained(controller),
page)) {
SetFocusBehavior(views::View::FocusBehavior::ACCESSIBLE_ONLY);
views::InstallFixedSizeCircleHighlightPathGenerator(this, kInkDropRadius);
const gfx::Point center = GetLocalBounds().CenterPoint();
const gfx::Rect bounds(center.x() - kInkDropRadius,
center.y() - kInkDropRadius, 2 * kInkDropRadius,
2 * kInkDropRadius);
StyleUtil::SetUpInkDropForButton(this, GetLocalBounds().InsetsFrom(bounds),
/*highlight_on_hover=*/true,
/*highlight_on_focus=*/false);
}
PageIndicatorButton(const PageIndicatorButton&) = delete;
PageIndicatorButton& operator=(const PageIndicatorButton&) = delete;
~PageIndicatorButton() override {}
void SetSelected(bool selected) {
if (selected == selected_)
return;
selected_ = selected;
SchedulePaint();
if (selected)
NotifyAccessibilityEvent(ax::mojom::Event::kAlert, true);
}
// views::View:
gfx::Size CalculatePreferredSize() const override {
return gfx::Size(kInkDropRadius * 2, kInkDropRadius * 2);
}
// views::Button:
const char* GetClassName() const override { return "PageIndicatorView"; }
// views::Button:
void PaintButtonContents(gfx::Canvas* canvas) override {
gfx::Rect rect(GetContentsBounds());
const SkColor selected_color =
AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kIconColorPrimary);
cc::PaintFlags flags;
flags.setAntiAlias(true);
flags.setStyle(cc::PaintFlags::kFill_Style);
flags.setColor(selected_ ? selected_color
: ColorUtil::GetDisabledColor(selected_color));
canvas->DrawCircle(rect.CenterPoint(), kUnifiedPageIndicatorButtonRadius,
flags);
}
// views::Button:
void OnThemeChanged() override {
views::Button::OnThemeChanged();
StyleUtil::ConfigureInkDropAttributes(
this, StyleUtil::kBaseColor | StyleUtil::kInkDropOpacity |
StyleUtil::kHighlightOpacity);
SchedulePaint();
}
bool selected() const { return selected_; }
protected:
// views::Button:
void NotifyClick(const ui::Event& event) override {
Button::NotifyClick(event);
views::InkDrop::Get(this)->GetInkDrop()->AnimateToState(
views::InkDropState::ACTION_TRIGGERED);
}
private:
bool selected_ = false;
};
PageIndicatorView::PageIndicatorView(UnifiedSystemTrayController* controller,
bool initially_expanded)
: controller_(controller),
model_(controller->model()->pagination_model()),
expanded_amount_(initially_expanded ? 1 : 0),
buttons_container_(new views::View) {
SetVisible(initially_expanded);
buttons_container_->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal, gfx::Insets()));
buttons_container_->SetPaintToLayer();
buttons_container_->layer()->SetFillsBoundsOpaquely(false);
AddChildView(buttons_container_.get());
TotalPagesChanged(0, model_->total_pages());
DCHECK(model_);
model_->AddObserver(this);
}
PageIndicatorView::~PageIndicatorView() {
model_->RemoveObserver(this);
}
gfx::Size PageIndicatorView::CalculatePreferredSize() const {
return gfx::Size(kTrayMenuWidth, kPageIndicatorViewMaxHeight);
}
void PageIndicatorView::Layout() {
gfx::Rect rect(GetContentsBounds());
gfx::Size buttons_container_size(buttons_container_->GetPreferredSize());
rect.ClampToCenteredSize(buttons_container_size);
buttons_container_->SetBoundsRect(rect);
}
const char* PageIndicatorView::GetClassName() const {
return "PageIndicatorView";
}
void PageIndicatorView::SetExpandedAmount(double expanded_amount) {
DCHECK(0.0 <= expanded_amount && expanded_amount <= 1.0);
SetVisible(expanded_amount > 0.0);
expanded_amount_ = expanded_amount;
// TODO(amehfooz): Confirm animation curve with UX.
buttons_container_->layer()->SetOpacity(
std::max(0., 6 * expanded_amount_ - 5.));
if (CalculatePreferredSize() != size())
InvalidateLayout();
}
int PageIndicatorView::GetExpandedHeight() {
return buttons_container_->GetPreferredSize().height();
}
void PageIndicatorView::TotalPagesChanged(int previous_page_count,
int new_page_count) {
DCHECK(model_);
buttons_container_->RemoveAllChildViews();
for (int i = 0; i < model_->total_pages(); ++i) {
PageIndicatorButton* button = new PageIndicatorButton(controller_, i);
button->SetAccessibleName(l10n_util::GetStringFUTF16(
IDS_APP_LIST_PAGE_SWITCHER, base::FormatNumber(i + 1),
base::FormatNumber(model_->total_pages())));
button->SetSelected(i == model_->selected_page());
buttons_container_->AddChildView(button);
}
buttons_container_->SetVisible(model_->total_pages() > 1);
Layout();
}
PageIndicatorView::PageIndicatorButton* PageIndicatorView::GetButtonByIndex(
int index) {
return static_cast<PageIndicatorButton*>(
buttons_container_->children().at(index));
}
void PageIndicatorView::SelectedPageChanged(int old_selected,
int new_selected) {
size_t total_children = buttons_container_->children().size();
if (old_selected >= 0 && static_cast<size_t>(old_selected) < total_children)
GetButtonByIndex(old_selected)->SetSelected(false);
if (new_selected >= 0 && static_cast<size_t>(new_selected) < total_children)
GetButtonByIndex(new_selected)->SetSelected(true);
}
bool PageIndicatorView::IsPageSelectedForTesting(int index) {
return GetButtonByIndex(index)->selected();
}
} // namespace ash

@ -1,73 +0,0 @@
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_SYSTEM_UNIFIED_PAGE_INDICATOR_VIEW_H_
#define ASH_SYSTEM_UNIFIED_PAGE_INDICATOR_VIEW_H_
#include "ash/ash_export.h"
#include "ash/public/cpp/pagination/pagination_model_observer.h"
#include "base/memory/raw_ptr.h"
#include "ui/views/view.h"
namespace ash {
class PaginationModel;
class UnifiedSystemTrayController;
// PageIndicatorView represents its underlying PaginationModel with a button
// strip. Each page in the PaginationModel has a button in the strip and
// when the button is clicked, the corresponding page becomes selected.
class ASH_EXPORT PageIndicatorView : public views::View,
public PaginationModelObserver {
public:
PageIndicatorView(UnifiedSystemTrayController* controller,
bool initially_expanded);
PageIndicatorView(const PageIndicatorView&) = delete;
PageIndicatorView& operator=(const PageIndicatorView&) = delete;
~PageIndicatorView() override;
// Change the expanded state. 0.0 if collapsed, and 1.0 if expanded.
// Otherwise, it shows an intermediate state while animating.
void SetExpandedAmount(double expanded_amount);
// Returns the height of this view when the tray is fully expanded.
int GetExpandedHeight();
// views::View:
gfx::Size CalculatePreferredSize() const override;
void Layout() override;
const char* GetClassName() const override;
private:
friend class PageIndicatorViewTest;
class PageIndicatorButton;
// PaginationModelObserver:
void TotalPagesChanged(int previous_page_count, int new_page_count) override;
void SelectedPageChanged(int old_selected, int new_selected) override;
bool IsPageSelectedForTesting(int index);
views::View* buttons_container() { return buttons_container_; }
PageIndicatorButton* GetButtonByIndex(int index);
const raw_ptr<UnifiedSystemTrayController,
DanglingUntriaged | ExperimentalAsh>
controller_;
// Owned by UnifiedSystemTrayModel.
const raw_ptr<PaginationModel, ExperimentalAsh> model_;
double expanded_amount_;
// Owned by views hierarchy.
raw_ptr<views::View, ExperimentalAsh> buttons_container_;
};
} // namespace ash
#endif // ASH_SYSTEM_UNIFIED_PAGE_INDICATOR_VIEW_H_

@ -1,114 +0,0 @@
// Copyright 2019 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/page_indicator_view.h"
#include "ash/public/cpp/pagination/pagination_model.h"
#include "ash/session/test_session_controller_client.h"
#include "ash/system/unified/unified_system_tray_controller.h"
#include "ash/system/unified/unified_system_tray_model.h"
#include "ash/system/unified/unified_system_tray_view.h"
#include "ash/test/ash_test_base.h"
#include "base/memory/scoped_refptr.h"
namespace ash {
namespace {
int kPageCount = 10;
}
class PageIndicatorViewTest : public NoSessionAshTestBase {
public:
PageIndicatorViewTest() = default;
PageIndicatorViewTest(const PageIndicatorViewTest&) = delete;
PageIndicatorViewTest& operator=(const PageIndicatorViewTest&) = delete;
~PageIndicatorViewTest() override = default;
void SetUp() override {
NoSessionAshTestBase::SetUp();
model_ = base::MakeRefCounted<UnifiedSystemTrayModel>(nullptr);
controller_ = std::make_unique<UnifiedSystemTrayController>(model_.get());
unified_view_ = std::make_unique<UnifiedSystemTrayView>(
controller_.get(), true /* initially_expanded */);
}
void TearDown() override {
controller_.reset();
unified_view_.reset();
model_.reset();
NoSessionAshTestBase::TearDown();
}
protected:
int GetButtonCount() {
return page_indicator_view()->buttons_container()->children().size();
}
bool IsPageSelected(int index) {
return page_indicator_view()->IsPageSelectedForTesting(index);
}
PaginationModel* pagination_model() { return model_->pagination_model(); }
PageIndicatorView* page_indicator_view() {
return unified_view_->page_indicator_view_for_test();
}
UnifiedSystemTrayView* unified_view() { return unified_view_.get(); }
private:
scoped_refptr<UnifiedSystemTrayModel> model_;
std::unique_ptr<UnifiedSystemTrayController> controller_;
std::unique_ptr<UnifiedSystemTrayView> unified_view_;
};
// Number of buttons is equal to total pages in PaginationModel.
TEST_F(PageIndicatorViewTest, ButtonForEachPage) {
for (int i = 0; i < kPageCount; i++) {
pagination_model()->SetTotalPages(i);
EXPECT_EQ(i, GetButtonCount());
}
}
// Single button corresponding to page in PaginationModel is set to selected.
TEST_F(PageIndicatorViewTest, SelectPage) {
pagination_model()->SetTotalPages(kPageCount);
for (int i = 0; i < kPageCount; i++) {
pagination_model()->SelectPage(i, false /* animate */);
EXPECT_TRUE(IsPageSelected(i));
for (int j = 0; j < kPageCount; j++) {
if (i == j)
continue;
EXPECT_FALSE(IsPageSelected(j));
}
}
}
TEST_F(PageIndicatorViewTest, ExpandAndCollapse) {
int cur_height;
int prev_height;
double expanded_increments[] = {0.90, 0.75, 0.5, 0.25, 0.10};
pagination_model()->SetTotalPages(kPageCount);
// PageIndicatorView has decreasing height as the expanded amount is
// decreased.
prev_height = page_indicator_view()->GetContentsBounds().height();
for (double i : expanded_increments) {
unified_view()->SetExpandedAmount(i);
cur_height = page_indicator_view()->GetContentsBounds().height();
EXPECT_GE(prev_height, cur_height);
prev_height = cur_height;
}
// PageIndicatorView has zero height when collapsed.
unified_view()->SetExpandedAmount(0.00);
EXPECT_EQ(page_indicator_view()->GetContentsBounds().height(), 0);
}
} // namespace ash

@ -14,13 +14,10 @@
#include "ash/system/tray/tray_constants.h"
#include "ash/system/tray/tray_detailed_view.h"
#include "ash/system/unified/detailed_view_controller.h"
#include "ash/system/unified/feature_pod_button.h"
#include "ash/system/unified/feature_tile.h"
#include "ash/system/unified/feature_tiles_container_view.h"
#include "ash/system/unified/page_indicator_view.h"
#include "ash/system/unified/quick_settings_footer.h"
#include "ash/system/unified/quick_settings_header.h"
#include "ash/system/unified/unified_system_info_view.h"
#include "ash/system/unified/unified_system_tray_controller.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
@ -302,12 +299,6 @@ void QuickSettingsView::TotalPagesChanged(int previous_page_count,
pagination_view_->SetVisible(new_page_count > 1);
}
void QuickSettingsView::OnGestureEvent(ui::GestureEvent* event) {
if (event->type() == ui::ET_SCROLL_FLING_START) {
controller_->Fling(event->details().velocity_y());
}
}
BEGIN_METADATA(QuickSettingsView, views::View)
END_METADATA

@ -100,9 +100,6 @@ class ASH_EXPORT QuickSettingsView : public views::View,
// PaginationModelObserver:
void TotalPagesChanged(int previous_page_count, int new_page_count) override;
// views::View:
void OnGestureEvent(ui::GestureEvent* event) override;
FeatureTilesContainerView* feature_tiles_container() {
return feature_tiles_container_;
}

@ -59,10 +59,6 @@ bool SystemTrayTestApi::IsTrayBubbleOpen() {
return GetTray()->IsBubbleShown();
}
bool SystemTrayTestApi::IsTrayBubbleExpanded() {
return GetTray()->bubble_->controller_->IsExpanded();
}
void SystemTrayTestApi::ShowBubble() {
GetTray()->ShowBubble();
}
@ -71,14 +67,6 @@ void SystemTrayTestApi::CloseBubble() {
GetTray()->CloseBubble();
}
void SystemTrayTestApi::CollapseBubble() {
GetTray()->EnsureQuickSettingsCollapsed(true /*animate*/);
}
void SystemTrayTestApi::ExpandBubble() {
GetTray()->EnsureBubbleExpanded();
}
void SystemTrayTestApi::ShowAccessibilityDetailedView() {
GetTray()->ShowBubble();
GetTray()->bubble_->controller_->ShowAccessibilityDetailedView();
@ -86,7 +74,7 @@ void SystemTrayTestApi::ShowAccessibilityDetailedView() {
void SystemTrayTestApi::ShowNetworkDetailedView() {
GetTray()->ShowBubble();
GetTray()->bubble_->controller_->ShowNetworkDetailedView(true /* force */);
GetTray()->bubble_->controller_->ShowNetworkDetailedView();
}
AccessibilityDetailedView* SystemTrayTestApi::GetAccessibilityDetailedView() {

@ -1,330 +0,0 @@
// 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/top_shortcuts_view.h"
#include <algorithm>
#include <cstddef>
#include <memory>
#include <numeric>
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/quick_settings_catalogs.h"
#include "ash/public/cpp/ash_view_ids.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/shutdown_controller_impl.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/icon_button.h"
#include "ash/style/pill_button.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/tray/tray_popup_utils.h"
#include "ash/system/unified/buttons.h"
#include "ash/system/unified/collapse_button.h"
#include "ash/system/unified/quick_settings_metrics_util.h"
#include "ash/system/unified/unified_system_tray_controller.h"
#include "ash/system/unified/user_chooser_detailed_view_controller.h"
#include "ash/system/unified/user_chooser_view.h"
#include "ash/system/user/login_status.h"
#include "base/functional/bind.h"
#include "base/ranges/algorithm.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/vector_icons/vector_icons.h"
#include "ui/color/color_id.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/highlight_path_generator.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h"
namespace ash {
TopShortcutButtonContainer::TopShortcutButtonContainer() = default;
TopShortcutButtonContainer::~TopShortcutButtonContainer() = default;
// Buttons are equally spaced by the default value, but the gap will be
// narrowed evenly when the parent view is not large enough.
void TopShortcutButtonContainer::Layout() {
const gfx::Rect child_area = GetContentsBounds();
views::View::Views visible_children;
base::ranges::copy_if(
children(), std::back_inserter(visible_children), [](const auto* v) {
return v->GetVisible() && (v->GetPreferredSize().width() > 0);
});
if (visible_children.empty()) {
return;
}
const int visible_child_width =
std::accumulate(visible_children.cbegin(), visible_children.cend(), 0,
[](int width, const auto* v) {
return width + v->GetPreferredSize().width();
});
int spacing = 0;
if (visible_children.size() > 1) {
spacing = (child_area.width() - visible_child_width) /
(static_cast<int>(visible_children.size()) - 1);
spacing = std::clamp(spacing, kUnifiedTopShortcutButtonMinSpacing,
kUnifiedTopShortcutButtonDefaultSpacing);
}
int x = child_area.x();
int y = child_area.y() + kUnifiedTopShortcutContainerTopPadding +
kUnifiedCircularButtonFocusPadding.bottom();
for (auto* child : visible_children) {
int child_y = y;
int width = child->GetPreferredSize().width();
if (child == user_avatar_button_) {
x -= kUnifiedCircularButtonFocusPadding.left();
child_y -= kUnifiedCircularButtonFocusPadding.bottom();
} else if (child == sign_out_button_) {
// When there's not enough space, shrink the sign-out button.
const int remainder =
child_area.width() -
(static_cast<int>(visible_children.size()) - 1) * spacing -
(visible_child_width - width);
width = std::clamp(width, 0, std::max(0, remainder));
}
child->SetBounds(x, child_y, width, child->GetHeightForWidth(width));
x += width + spacing;
if (child == user_avatar_button_) {
x -= kUnifiedCircularButtonFocusPadding.right();
}
}
}
gfx::Size TopShortcutButtonContainer::CalculatePreferredSize() const {
int total_horizontal_size = 0;
int num_visible = 0;
for (const auto* child : children()) {
if (!child->GetVisible()) {
continue;
}
int child_horizontal_size = child->GetPreferredSize().width();
if (child_horizontal_size == 0) {
continue;
}
total_horizontal_size += child_horizontal_size;
num_visible++;
}
int width =
(num_visible == 0)
? 0
: total_horizontal_size +
(num_visible - 1) * kUnifiedTopShortcutButtonDefaultSpacing;
int height = kTrayItemSize + kUnifiedCircularButtonFocusPadding.height() +
kUnifiedTopShortcutContainerTopPadding;
return gfx::Size(width, height);
}
const char* TopShortcutButtonContainer::GetClassName() const {
return "TopShortcutButtonContainer";
}
views::View* TopShortcutButtonContainer::AddUserAvatarButton(
std::unique_ptr<views::View> user_avatar_button) {
user_avatar_button_ = AddChildView(std::move(user_avatar_button));
return user_avatar_button_;
}
views::Button* TopShortcutButtonContainer::AddSignOutButton(
std::unique_ptr<views::Button> sign_out_button) {
sign_out_button_ = AddChildView(std::move(sign_out_button));
return sign_out_button_;
}
TopShortcutsView::TopShortcutsView(UnifiedSystemTrayController* controller) {
DCHECK(controller);
#if DCHECK_IS_ON()
// Only need it for `DCHECK` in `OnChildViewAdded`.
AddObserver(this);
#endif // DCHECK_IS_ON()
auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal, kUnifiedTopShortcutPadding,
kUnifiedTopShortcutSpacing));
layout->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kStart);
auto button_container = std::make_unique<TopShortcutButtonContainer>();
Shell* shell = Shell::Get();
bool is_on_login_screen =
shell->session_controller()->login_status() == LoginStatus::NOT_LOGGED_IN;
bool can_show_settings = TrayPopupUtils::CanOpenWebUISettings();
bool can_lock_screen = shell->session_controller()->CanLockScreen();
if (!is_on_login_screen) {
user_avatar_button_ = button_container->AddUserAvatarButton(
std::make_unique<UserAvatarButton>(base::BindRepeating(
[](UnifiedSystemTrayController* controller) {
quick_settings_metrics_util::RecordQsButtonActivated(
QsButtonCatalogName::kAvatarButton);
controller->ShowUserChooserView();
},
base::Unretained(controller))));
user_avatar_button_->SetEnabled(
UserChooserDetailedViewController::IsUserChooserEnabled());
user_avatar_button_->SetID(VIEW_ID_QS_USER_AVATAR_BUTTON);
sign_out_button_ =
button_container->AddSignOutButton(std::make_unique<PillButton>(
base::BindRepeating(
[](UnifiedSystemTrayController* controller) {
quick_settings_metrics_util::RecordQsButtonActivated(
QsButtonCatalogName::kSignOutButton);
controller->HandleSignOutAction();
},
base::Unretained(controller)),
user::GetLocalizedSignOutStringForStatus(
Shell::Get()->session_controller()->login_status(),
/*multiline=*/false),
PillButton::Type::kDefaultWithoutIcon,
/*icon=*/nullptr));
sign_out_button_->SetID(VIEW_ID_QS_SIGN_OUT_BUTTON);
}
bool reboot = shell->shutdown_controller()->reboot_on_shutdown();
power_button_ = button_container->AddChildView(std::make_unique<IconButton>(
base::BindRepeating(
[](UnifiedSystemTrayController* controller) {
quick_settings_metrics_util::RecordQsButtonActivated(
QsButtonCatalogName::kPowerButton);
controller->HandlePowerAction();
},
base::Unretained(controller)),
IconButton::Type::kMedium, &kUnifiedMenuPowerIcon,
reboot ? IDS_ASH_STATUS_TRAY_REBOOT : IDS_ASH_STATUS_TRAY_SHUTDOWN));
power_button_->SetID(VIEW_ID_QS_POWER_BUTTON);
if (can_show_settings && can_lock_screen) {
lock_button_ = button_container->AddChildView(std::make_unique<IconButton>(
base::BindRepeating(
[](UnifiedSystemTrayController* controller) {
quick_settings_metrics_util::RecordQsButtonActivated(
QsButtonCatalogName::kLockButton);
controller->HandleLockAction();
},
base::Unretained(controller)),
IconButton::Type::kMedium, &kUnifiedMenuLockIcon,
IDS_ASH_STATUS_TRAY_LOCK));
lock_button_->SetID(VIEW_ID_QS_LOCK_BUTTON);
}
if (can_show_settings) {
settings_button_ =
button_container->AddChildView(std::make_unique<IconButton>(
base::BindRepeating(
[](UnifiedSystemTrayController* controller) {
quick_settings_metrics_util::RecordQsButtonActivated(
QsButtonCatalogName::kSettingsButton);
controller->HandleSettingsAction();
},
base::Unretained(controller)),
IconButton::Type::kMedium, &vector_icons::kSettingsOutlineIcon,
IDS_ASH_STATUS_TRAY_SETTINGS));
settings_button_->SetID(VIEW_ID_QS_SETTINGS_BUTTON);
local_state_pref_change_registrar_.Init(Shell::Get()->local_state());
local_state_pref_change_registrar_.Add(
prefs::kOsSettingsEnabled,
base::BindRepeating(&TopShortcutsView::UpdateSettingsButtonState,
base::Unretained(this)));
UpdateSettingsButtonState();
}
container_ = AddChildView(std::move(button_container));
// |collapse_button_| should be right-aligned, so we make the buttons
// container flex occupying all remaining space.
layout->SetFlexForView(container_, 1);
if (features::IsQsRevampEnabled()) {
return;
}
auto collapse_button_container = std::make_unique<views::View>();
collapse_button_ = collapse_button_container->AddChildView(
std::make_unique<CollapseButton>(base::BindRepeating(
[](UnifiedSystemTrayController* controller) {
quick_settings_metrics_util::RecordQsButtonActivated(
QsButtonCatalogName::kCollapseButton);
controller->ToggleExpanded();
},
base::Unretained(controller))));
collapse_button_->SetID(VIEW_ID_QS_COLLAPSE_BUTTON);
const gfx::Size collapse_button_size = collapse_button_->GetPreferredSize();
collapse_button_container->SetPreferredSize(
gfx::Size(collapse_button_size.width(),
collapse_button_size.height() + kUnifiedTopShortcutSpacing));
collapse_button_->SetBoundsRect(gfx::Rect(
gfx::Point(0, kUnifiedTopShortcutSpacing), collapse_button_size));
AddChildView(std::move(collapse_button_container));
}
TopShortcutsView::~TopShortcutsView() {
#if DCHECK_IS_ON()
// Only need it for `DCHECK` in `OnChildViewAdded`.
RemoveObserver(this);
#endif // DCHECK_IS_ON()
}
// static
void TopShortcutsView::RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(prefs::kOsSettingsEnabled, true);
}
void TopShortcutsView::SetExpandedAmount(double expanded_amount) {
if (features::IsQsRevampEnabled()) {
return;
}
collapse_button_->SetExpandedAmount(expanded_amount);
}
const char* TopShortcutsView::GetClassName() const {
return "TopShortcutsView";
}
void TopShortcutsView::OnChildViewAdded(View* observed_view, View* child) {
if (observed_view != this) {
return;
}
if (child->children().empty()) {
DCHECK(child->GetID() >= VIEW_ID_QS_MIN && child->GetID() <= VIEW_ID_QS_MAX)
<< "All buttons directly added to this view must have a view ID with "
"VIEW_ID_QS_*, and record a metric using QsButtonCatalogName (see "
"other usages of the catalog names for an example)";
return;
}
for (View* button : child->children()) {
DCHECK(button->GetID() >= VIEW_ID_QS_MIN &&
button->GetID() <= VIEW_ID_QS_MAX)
<< "All buttons directly added to each container must have a view ID "
"with VIEW_ID_QS_*, and record a metric using QsButtonCatalogName "
"(see other usages of the catalog names for an example)";
}
}
void TopShortcutsView::UpdateSettingsButtonState() {
PrefService* const local_state = Shell::Get()->local_state();
const bool settings_icon_enabled =
local_state->GetBoolean(prefs::kOsSettingsEnabled);
settings_button_->SetState(settings_icon_enabled
? views::Button::STATE_NORMAL
: views::Button::STATE_DISABLED);
}
} // namespace ash

@ -1,98 +0,0 @@
// 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.
#ifndef ASH_SYSTEM_UNIFIED_TOP_SHORTCUTS_VIEW_H_
#define ASH_SYSTEM_UNIFIED_TOP_SHORTCUTS_VIEW_H_
#include "ash/accessibility/accessibility_observer.h"
#include "ash/ash_export.h"
#include "base/memory/raw_ptr.h"
#include "components/prefs/pref_change_registrar.h"
#include "ui/views/view.h"
#include "ui/views/view_observer.h"
class PrefRegistrySimple;
namespace views {
class Button;
}
namespace ash {
class CollapseButton;
class IconButton;
class TopShortcutsViewTest;
class UnifiedSystemTrayController;
// Container for the top shortcut buttons. The view may narrow gaps between
// buttons when there's not enough space. When those doesn't fit in the view
// even after that, the sign-out button will be resized.
class TopShortcutButtonContainer : public views::View {
public:
TopShortcutButtonContainer();
TopShortcutButtonContainer(const TopShortcutButtonContainer&) = delete;
TopShortcutButtonContainer& operator=(const TopShortcutButtonContainer&) =
delete;
~TopShortcutButtonContainer() override;
// views::View:
void Layout() override;
gfx::Size CalculatePreferredSize() const override;
const char* GetClassName() const override;
views::View* AddUserAvatarButton(
std::unique_ptr<views::View> user_avatar_button);
// Add the sign-out button, which can be resized upon layout.
views::Button* AddSignOutButton(
std::unique_ptr<views::Button> sign_out_button);
private:
raw_ptr<views::View, ExperimentalAsh> user_avatar_button_ = nullptr;
raw_ptr<views::Button, ExperimentalAsh> sign_out_button_ = nullptr;
};
// Top shortcuts view shown on the top of UnifiedSystemTrayView.
class ASH_EXPORT TopShortcutsView : public views::View,
public views::ViewObserver {
public:
explicit TopShortcutsView(UnifiedSystemTrayController* controller);
TopShortcutsView(const TopShortcutsView&) = delete;
TopShortcutsView& operator=(const TopShortcutsView&) = delete;
~TopShortcutsView() override;
static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
// Change the expanded state. CollapseButton icon will rotate.
void SetExpandedAmount(double expanded_amount);
// views::View
const char* GetClassName() const override;
private:
friend class TopShortcutsViewTest;
// views::ViewObserver:
void OnChildViewAdded(View* observed_view, View* child) override;
// Disables/Enables the |settings_button_| based on kSettingsIconEnabled pref.
void UpdateSettingsButtonState();
// Owned by views hierarchy.
raw_ptr<views::View, ExperimentalAsh> user_avatar_button_ = nullptr;
raw_ptr<views::Button, ExperimentalAsh> sign_out_button_ = nullptr;
raw_ptr<TopShortcutButtonContainer, ExperimentalAsh> container_ = nullptr;
raw_ptr<IconButton, ExperimentalAsh> lock_button_ = nullptr;
raw_ptr<IconButton, ExperimentalAsh> settings_button_ = nullptr;
raw_ptr<IconButton, ExperimentalAsh> power_button_ = nullptr;
raw_ptr<CollapseButton, ExperimentalAsh> collapse_button_ = nullptr;
PrefChangeRegistrar local_state_pref_change_registrar_;
};
} // namespace ash
#endif // ASH_SYSTEM_UNIFIED_TOP_SHORTCUTS_VIEW_H_

@ -254,7 +254,9 @@ void UnifiedSliderBubbleController::ShowBubble(SliderType slider_type) {
}
if (IsAnyMainBubbleShown()) {
tray_->EnsureBubbleExpanded();
// If a detailed view is showing, first transit to the main view.
tray_->bubble()->unified_system_tray_controller()->TransitionToMainView(
false);
// Unlike VOLUME and BRIGHTNESS, which are shown in the main bubble view,
// MIC slider is shown in the audio details view.

@ -1,530 +0,0 @@
// 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_info_view.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/quick_settings_catalogs.h"
#include "ash/public/cpp/ash_view_ids.h"
#include "ash/public/cpp/system_tray_client.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/shell_delegate.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/system/channel_indicator/channel_indicator_quick_settings_view.h"
#include "ash/system/channel_indicator/channel_indicator_utils.h"
#include "ash/system/model/clock_model.h"
#include "ash/system/model/clock_observer.h"
#include "ash/system/model/system_tray_model.h"
#include "ash/system/model/update_model.h"
#include "ash/system/power/adaptive_charging_controller.h"
#include "ash/system/power/power_status.h"
#include "ash/system/time/calendar_metrics.h"
#include "ash/system/tray/system_tray_notifier.h"
#include "ash/system/tray/tray_popup_utils.h"
#include "ash/system/unified/buttons.h"
#include "ash/system/unified/quick_settings_metrics_util.h"
#include "ash/system/update/eol_notice_quick_settings_view.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/i18n/time_formatting.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/color/color_id.h"
#include "ui/compositor/layer.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/animation/ink_drop.h"
#include "ui/views/background.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/focus_ring.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/separator.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h"
namespace ash {
using ContentLayerType = AshColorProvider::ContentLayerType;
namespace {
constexpr auto kBatteryLabelViewInsets = gfx::Insets(2);
// Helper function for getting the content layer color.
inline SkColor GetContentLayerColor(ContentLayerType type) {
return AshColorProvider::Get()->GetContentLayerColor(type);
}
// Helper function for configuring label in `BatteryLabelView` and
// `BatteryIconView`.
void ConfigureLabel(views::Label* label, SkColor color) {
label->SetAutoColorReadabilityEnabled(false);
label->SetSubpixelRenderingEnabled(false);
label->SetEnabledColor(color);
label->GetViewAccessibility().OverrideIsIgnored(true);
}
// A base class for both `BatteryLabelView` and `BatteryIconView`. It updates by
// observing `PowerStatus`.
class BatteryInfoViewBase : public views::Button, public PowerStatus::Observer {
public:
explicit BatteryInfoViewBase(UnifiedSystemTrayController* controller)
: Button(base::BindRepeating(
[](UnifiedSystemTrayController* controller) {
quick_settings_metrics_util::RecordQsButtonActivated(
QsButtonCatalogName::kBatteryButton);
controller->HandleOpenPowerSettingsAction();
},
controller)) {
PowerStatus::Get()->AddObserver(this);
}
BatteryInfoViewBase(const BatteryInfoViewBase&) = delete;
BatteryInfoViewBase& operator=(const BatteryInfoViewBase&) = delete;
~BatteryInfoViewBase() override { PowerStatus::Get()->RemoveObserver(this); }
// Updates the subclass view's ui when `OnPowerStatusChanged`.
virtual void Update() = 0;
private:
// views::View:
void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
node_data->role = ax::mojom::Role::kLabelText;
node_data->SetName(
PowerStatus::Get()->GetAccessibleNameString(/*full_description=*/true));
}
void ChildPreferredSizeChanged(views::View* child) override {
PreferredSizeChanged();
}
void ChildVisibilityChanged(views::View* child) override {
PreferredSizeChanged();
}
// PowerStatus::Observer:
void OnPowerStatusChanged() override { Update(); }
};
// A view that shows battery status.
class BatteryLabelView : public BatteryInfoViewBase {
public:
BatteryLabelView(UnifiedSystemTrayController* controller,
bool use_smart_charging_ui)
: BatteryInfoViewBase(controller),
use_smart_charging_ui_(use_smart_charging_ui) {
SetID(VIEW_ID_QS_BATTERY_BUTTON);
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal, kBatteryLabelViewInsets));
views::FocusRing::Get(this)->SetColorId(
static_cast<ui::ColorId>(ui::kColorAshFocusRing));
percentage_ = AddChildView(std::make_unique<views::Label>());
auto separator = std::make_unique<views::Label>();
separator->SetText(l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_BATTERY_STATUS_SEPARATOR));
separator_view_ = AddChildView(std::move(separator));
status_ = AddChildView(std::make_unique<views::Label>());
Update();
}
BatteryLabelView(const BatteryLabelView&) = delete;
BatteryLabelView& operator=(const BatteryLabelView&) = delete;
~BatteryLabelView() override = default;
private:
// views::View:
void OnThemeChanged() override {
views::View::OnThemeChanged();
const auto color =
GetContentLayerColor(ContentLayerType::kTextColorSecondary);
ConfigureLabel(percentage_, color);
ConfigureLabel(separator_view_, color);
ConfigureLabel(status_, color);
}
// BatteryInfoViewBase:
void Update() override {
std::u16string percentage_text;
std::u16string status_text;
std::tie(percentage_text, status_text) =
PowerStatus::Get()->GetStatusStrings();
percentage_->SetText(percentage_text);
status_->SetText(status_text);
percentage_->SetVisible(!percentage_text.empty() &&
!use_smart_charging_ui_);
separator_view_->SetVisible(!percentage_text.empty() &&
!use_smart_charging_ui_ &&
!status_text.empty());
status_->SetVisible(!status_text.empty());
}
// Owned by this view, which is owned by views hierarchy.
raw_ptr<views::Label, ExperimentalAsh> percentage_ = nullptr;
raw_ptr<views::Label, ExperimentalAsh> separator_view_ = nullptr;
raw_ptr<views::Label, ExperimentalAsh> status_ = nullptr;
// If true, this view will only show the status and let the `BatteryIconView`
// show the rest. If false, the `percentage_` and separator will be visible.
// Smart charging means `ash::features::IsAdaptiveChargingEnabled()` and it
// is adaptive delaying charge.
const bool use_smart_charging_ui_;
};
// A view that shows battery icon and charging state when smart charging is
// enabled.
class BatteryIconView : public BatteryInfoViewBase {
public:
explicit BatteryIconView(UnifiedSystemTrayController* controller)
: BatteryInfoViewBase(controller) {
SetID(VIEW_ID_QS_BATTERY_BUTTON);
auto layout = std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal);
layout->set_inside_border_insets(kUnifiedSystemInfoBatteryIconPadding);
SetLayoutManager(std::move(layout));
battery_image_ = AddChildView(std::make_unique<views::ImageView>());
// The battery icon requires its own layer to properly render the masked
// outline of the badge within the battery icon.
battery_image_->SetPaintToLayer();
battery_image_->layer()->SetFillsBoundsOpaquely(false);
ConfigureIcon();
percentage_ = AddChildView(std::make_unique<views::Label>());
SetBackground(views::CreateRoundedRectBackground(
GetContentLayerColor(
ContentLayerType::kBatterySystemInfoBackgroundColor),
GetPreferredSize().height() / 2));
Update();
}
BatteryIconView(const BatteryIconView&) = delete;
BatteryIconView& operator=(const BatteryIconView&) = delete;
~BatteryIconView() override = default;
private:
// views::View:
void OnThemeChanged() override {
views::View::OnThemeChanged();
const auto color =
GetContentLayerColor(ContentLayerType::kButtonLabelColorPrimary);
ConfigureLabel(percentage_, color);
ConfigureIcon();
}
// BatteryInfoViewBase:
void Update() override {
const std::u16string percentage_text =
PowerStatus::Get()->GetStatusStrings().first;
percentage_->SetText(percentage_text);
percentage_->SetVisible(!percentage_text.empty());
ConfigureIcon();
}
// Builds the battery icon image.
void ConfigureIcon() {
const SkColor battery_icon_color =
GetContentLayerColor(ContentLayerType::kBatterySystemInfoIconColor);
const SkColor badge_color = GetContentLayerColor(
ContentLayerType::kBatterySystemInfoBackgroundColor);
PowerStatus::BatteryImageInfo info =
PowerStatus::Get()->GenerateBatteryImageInfo(battery_icon_color,
badge_color);
info.alert_if_low = false;
battery_image_->SetImage(PowerStatus::GetBatteryImage(
info, kUnifiedTrayBatteryIconSize, battery_image_->GetColorProvider()));
}
// Owned by this view, which is owned by views hierarchy.
raw_ptr<views::Label, ExperimentalAsh> percentage_ = nullptr;
raw_ptr<views::ImageView, ExperimentalAsh> battery_image_ = nullptr;
};
std::u16string FormatDate(const base::Time& time) {
// Use 'short' month format (e.g., "Oct") followed by non-padded day of
// month (e.g., "2", "10").
return base::LocalizedTimeFormatWithPattern(time, "LLLd");
}
std::u16string FormatDayOfWeek(const base::Time& time) {
// Use 'short' day of week format (e.g., "Wed").
return base::LocalizedTimeFormatWithPattern(time, "EEE");
}
// Returns whether SmartChargingUI should be used.
bool UseSmartChargingUI() {
return ash::features::IsAdaptiveChargingEnabled() &&
Shell::Get()
->adaptive_charging_controller()
->is_adaptive_delaying_charge();
}
// A view that shows current date in short format e.g. "Mon, Mar 12". It updates
// by observing `ClockObserver`.
class DateView : public views::Button, public ClockObserver {
public:
explicit DateView(UnifiedSystemTrayController* controller);
DateView(const DateView&) = delete;
DateView& operator=(const DateView&) = delete;
~DateView() override;
// views::Button:
const char* GetClassName() const override { return "DateView"; }
void OnThemeChanged() override;
private:
friend class ash::UnifiedSystemInfoView;
// Callback called when this is pressed.
void OnButtonPressed(const ui::Event& event);
void Update();
// views::Button:
gfx::Insets GetInsets() const override;
// ClockObserver:
void OnDateFormatChanged() override;
void OnSystemClockTimeUpdated() override;
void OnSystemClockCanSetTimeChanged(bool can_set_time) override;
void Refresh() override;
// Owned by the views hierarchy.
raw_ptr<views::Label, ExperimentalAsh> label_;
// Unowned.
const raw_ptr<UnifiedSystemTrayController,
DanglingUntriaged | ExperimentalAsh>
controller_;
};
DateView::DateView(UnifiedSystemTrayController* controller)
: Button(base::BindRepeating(&DateView::OnButtonPressed,
base::Unretained(this))),
label_(AddChildView(std::make_unique<views::Label>())),
controller_(controller) {
SetID(VIEW_ID_QS_DATE_VIEW_BUTTON);
SetLayoutManager(std::make_unique<views::FillLayout>());
label_->SetAutoColorReadabilityEnabled(false);
label_->SetSubpixelRenderingEnabled(false);
Update();
Shell::Get()->system_tray_model()->clock()->AddObserver(this);
SetInstallFocusRingOnFocus(true);
views::FocusRing::Get(this)->SetColorId(ui::kColorAshFocusRing);
views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::OFF);
}
DateView::~DateView() {
Shell::Get()->system_tray_model()->clock()->RemoveObserver(this);
}
void DateView::OnThemeChanged() {
views::Button::OnThemeChanged();
auto* color_provider = AshColorProvider::Get();
label_->SetEnabledColor(color_provider->GetContentLayerColor(
ContentLayerType::kTextColorPrimary));
}
void DateView::OnButtonPressed(const ui::Event& event) {
quick_settings_metrics_util::RecordQsButtonActivated(
QsButtonCatalogName::kDateViewButton);
if (controller_->IsExpanded()) {
controller_->ShowCalendarView(
calendar_metrics::CalendarViewShowSource::kDateView,
calendar_metrics::GetEventType(event));
return;
}
controller_->HandleOpenDateTimeSettingsAction();
}
void DateView::Update() {
base::Time now = base::Time::Now();
label_->SetText(l10n_util::GetStringFUTF16(
IDS_ASH_STATUS_TRAY_DATE, FormatDayOfWeek(now), FormatDate(now)));
SetAccessibleName(
l10n_util::GetStringFUTF16(IDS_ASH_CALENDAR_ENTRY_ACCESSIBLE_DESCRIPTION,
TimeFormatFriendlyDateAndTime(now)));
}
gfx::Insets DateView::GetInsets() const {
// This padding provides room to render the focus ring around this button.
return kUnifiedSystemInfoDateViewPadding;
}
void DateView::OnDateFormatChanged() {}
void DateView::OnSystemClockTimeUpdated() {
Update();
}
void DateView::OnSystemClockCanSetTimeChanged(bool can_set_time) {}
void DateView::Refresh() {
Update();
}
} // namespace
// A view that contains date, battery status, and whether the device
// is enterprise managed.
class ManagementPowerDateComboView : public views::View {
public:
explicit ManagementPowerDateComboView(
UnifiedSystemTrayController* controller) {
auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal, gfx::Insets(),
kUnifiedSystemInfoSpacing));
layout->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kCenter);
date_view_ = AddChildView(std::make_unique<DateView>(controller));
if (PowerStatus::Get()->IsBatteryPresent()) {
separator_view_ = AddChildView(std::make_unique<views::Separator>());
separator_view_->SetColorId(ui::kColorAshSystemUIMenuSeparator);
separator_view_->SetPreferredLength(kUnifiedSystemInfoHeight);
const bool use_smart_charging_ui = UseSmartChargingUI();
if (use_smart_charging_ui) {
AddChildView(std::make_unique<BatteryIconView>(controller));
}
AddChildView(std::make_unique<BatteryLabelView>(controller,
use_smart_charging_ui));
}
auto* spacing = AddChildView(std::make_unique<views::View>());
layout->SetFlexForView(spacing, 1);
enterprise_managed_view_ =
AddChildView(std::make_unique<EnterpriseManagedView>(controller));
supervised_view_ = AddChildView(std::make_unique<SupervisedUserView>());
}
ManagementPowerDateComboView(const ManagementPowerDateComboView&) = delete;
ManagementPowerDateComboView& operator=(const ManagementPowerDateComboView&) =
delete;
~ManagementPowerDateComboView() override = default;
bool IsSupervisedVisibleForTesting() {
return supervised_view_->GetVisible();
}
private:
friend class UnifiedSystemInfoView;
// Pointer to the actual child view is maintained for unit testing, owned by
// `ManagementPowerDateComboView`.
raw_ptr<EnterpriseManagedView, ExperimentalAsh> enterprise_managed_view_ =
nullptr;
// Pointer to the actual child view is maintained for unit testing, owned by
// `ManagementPowerDateComboView`.
raw_ptr<SupervisedUserView, ExperimentalAsh> supervised_view_ = nullptr;
// Separator between date and battery views, owned by
// `ManagementPowerDateComboView`.
raw_ptr<views::Separator, ExperimentalAsh> separator_view_ = nullptr;
// Pointer to the actual child view is maintained for unit testing, owned by
// `ManagementPowerDateComboView`.
raw_ptr<DateView, ExperimentalAsh> date_view_ = nullptr;
};
UnifiedSystemInfoView::UnifiedSystemInfoView(
UnifiedSystemTrayController* controller) {
// Layout for the overall UnifiedSystemInfoView.
auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical, kUnifiedSystemInfoViewPadding,
kUnifiedSystemInfoSpacing));
// Allow children to stretch to fill the whole width of the parent. Some
// direct children are kStart aligned, others are kCenter aligned.
layout->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kStretch);
// Construct a ManagementPowerDateComboView and save off a raw pointer, to
// facilitate introspection needed for unit tests.
combo_view_ =
AddChildView(std::make_unique<ManagementPowerDateComboView>(controller));
layout->SetFlexForView(combo_view_, 1);
if (Shell::Get()->session_controller()->GetSessionState() ==
session_manager::SessionState::ACTIVE) {
if (Shell::Get()->system_tray_model()->update_model()->show_eol_notice()) {
auto* eol_notice_wrapper = AddChildView(std::make_unique<views::View>());
auto* eol_notice_layout = eol_notice_wrapper->SetLayoutManager(
std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal, gfx::Insets(),
kUnifiedSystemInfoSpacing));
eol_notice_layout->set_main_axis_alignment(
views::BoxLayout::MainAxisAlignment::kCenter);
eol_notice_layout->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kCenter);
eol_notice_ = eol_notice_wrapper->AddChildView(
std::make_unique<EolNoticeQuickSettingsView>());
}
// If the release track is not "stable" then channel indicator UI for quick
// settings is put up.
auto channel = Shell::Get()->shell_delegate()->GetChannel();
if (!eol_notice_ &&
channel_indicator_utils::IsDisplayableChannel(channel)) {
channel_view_ =
AddChildView(std::make_unique<ChannelIndicatorQuickSettingsView>(
channel, Shell::Get()
->system_tray_model()
->client()
->IsUserFeedbackEnabled()));
}
}
}
UnifiedSystemInfoView::~UnifiedSystemInfoView() = default;
void UnifiedSystemInfoView::ChildVisibilityChanged(views::View* child) {
Layout();
}
void UnifiedSystemInfoView::ChildPreferredSizeChanged(views::View* child) {
Layout();
}
bool UnifiedSystemInfoView::IsSupervisedVisibleForTesting() {
return combo_view_->IsSupervisedVisibleForTesting(); // IN-TEST
}
views::View* UnifiedSystemInfoView::GetDateViewForTesting() {
return combo_view_->date_view_;
}
views::View* UnifiedSystemInfoView::GetDateViewLabelForTesting() {
DCHECK(combo_view_->date_view_);
return combo_view_->date_view_->label_;
}
void UnifiedSystemInfoView::UpdateDateViewForTesting() {
DCHECK(combo_view_->date_view_);
combo_view_->date_view_->Update();
}
BEGIN_METADATA(UnifiedSystemInfoView, views::View)
END_METADATA
} // namespace ash

@ -1,69 +0,0 @@
// 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.
#ifndef ASH_SYSTEM_UNIFIED_UNIFIED_SYSTEM_INFO_VIEW_H_
#define ASH_SYSTEM_UNIFIED_UNIFIED_SYSTEM_INFO_VIEW_H_
#include "ash/ash_export.h"
#include "ash/system/unified/unified_system_tray_controller.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/views/view.h"
namespace ash {
class ManagementPowerDateComboView;
class ChannelIndicatorQuickSettingsView;
// A view at the bottom of UnifiedSystemTray bubble that shows system
// information.
class ASH_EXPORT UnifiedSystemInfoView : public views::View {
public:
METADATA_HEADER(UnifiedSystemInfoView);
explicit UnifiedSystemInfoView(UnifiedSystemTrayController* controller);
UnifiedSystemInfoView(const UnifiedSystemInfoView&) = delete;
UnifiedSystemInfoView& operator=(const UnifiedSystemInfoView&) = delete;
~UnifiedSystemInfoView() override;
// views::View:
void ChildPreferredSizeChanged(views::View* child) override;
void ChildVisibilityChanged(views::View* child) override;
// Introspection methods needed for unit tests.
bool IsSupervisedVisibleForTesting();
private:
FRIEND_TEST_ALL_PREFIXES(UnifiedSystemInfoViewTest, EnterpriseManagedVisible);
FRIEND_TEST_ALL_PREFIXES(UnifiedSystemInfoViewTest,
EnterpriseManagedVisibleForActiveDirectory);
FRIEND_TEST_ALL_PREFIXES(UnifiedSystemInfoViewTest,
EnterpriseUserManagedVisible);
FRIEND_TEST_ALL_PREFIXES(UnifiedSystemInfoViewTest,
UpdateFiresAccessibilityEvents);
FRIEND_TEST_ALL_PREFIXES(UnifiedSystemInfoViewNoSessionTest, ChildVisible);
// Raw pointer to the combo view (owned by `UnifiedSystemInfoView`) that
// facilitates introspection needed for unit tests.
raw_ptr<ManagementPowerDateComboView, ExperimentalAsh> combo_view_ = nullptr;
// Raw pointer to the channel indicator quick settings view (owned by
// `UnifiedSystemInfoView`) that facilitates introspection needed for unit
// tests.
raw_ptr<ChannelIndicatorQuickSettingsView, ExperimentalAsh> channel_view_ =
nullptr;
raw_ptr<views::View, ExperimentalAsh> eol_notice_ = nullptr;
// Introspection methods needed for unit tests.
views::View* GetDateViewForTesting();
views::View* GetDateViewLabelForTesting();
void UpdateDateViewForTesting();
};
} // namespace ash
#endif // ASH_SYSTEM_UNIFIED_UNIFIED_SYSTEM_INFO_VIEW_H_

@ -1,286 +0,0 @@
// 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_info_view.h"
#include "ash/public/cpp/ash_view_ids.h"
#include "ash/public/cpp/login_types.h"
#include "ash/public/cpp/system_tray_client.h"
#include "ash/public/cpp/test/test_system_tray_client.h"
#include "ash/session/session_controller_impl.h"
#include "ash/session/test_session_controller_client.h"
#include "ash/shell.h"
#include "ash/system/model/enterprise_domain_model.h"
#include "ash/system/model/system_tray_model.h"
#include "ash/system/unified/unified_system_tray_controller.h"
#include "ash/system/unified/unified_system_tray_model.h"
#include "ash/test/ash_test_base.h"
#include "ash/test_shell_delegate.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "components/version_info/channel.h"
#include "ui/views/test/ax_event_counter.h"
namespace ash {
// Tests are parameterized by the release track UI:
// - Whether the release track is a value other than "stable"
// - Whether EOL notice is expected to be shown.
// The release track UI only shows if both conditions are met.
//
// NOTE: For QsRevamp, see similar tests in QuickSettingsHeaderTest.
class UnifiedSystemInfoViewTest
: public AshTestBase,
public testing::WithParamInterface<std::tuple<bool, bool>> {
public:
UnifiedSystemInfoViewTest()
: AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
UnifiedSystemInfoViewTest(const UnifiedSystemInfoViewTest&) = delete;
UnifiedSystemInfoViewTest& operator=(const UnifiedSystemInfoViewTest&) =
delete;
~UnifiedSystemInfoViewTest() override = default;
void SetUp() override {
// Provide our own `TestShellDelegate`, with a non-stable channel set if
// the passed-in parameter dictates.
std::unique_ptr<TestShellDelegate> shell_delegate =
std::make_unique<TestShellDelegate>();
if (IsReleaseTrackNotStable())
shell_delegate->set_channel(version_info::Channel::BETA);
AshTestBase::SetUp(std::move(shell_delegate));
if (ShouldShowEolNotice()) {
Shell::Get()->system_tray_model()->SetShowEolNotice(true);
}
// Instantiate members.
model_ = base::MakeRefCounted<UnifiedSystemTrayModel>(nullptr);
controller_ = std::make_unique<UnifiedSystemTrayController>(model_.get());
auto info_view = std::make_unique<UnifiedSystemInfoView>(controller_.get());
info_view_ = info_view.get();
// Place the view in a large views::Widget so the buttons are clickable.
widget_ = CreateFramelessTestWidget();
widget_->SetFullscreen(true);
widget_->SetContentsView(std::move(info_view));
}
bool IsReleaseTrackNotStable() const { return std::get<0>(GetParam()); }
bool ShouldShowEolNotice() const { return std::get<1>(GetParam()); }
views::View* GetDateButton() {
return info_view_->GetViewByID(VIEW_ID_QS_DATE_VIEW_BUTTON);
}
views::View* GetBatteryButton() {
return info_view_->GetViewByID(VIEW_ID_QS_BATTERY_BUTTON);
}
views::View* GetManagedButton() {
return info_view_->GetViewByID(VIEW_ID_QS_MANAGED_BUTTON);
}
views::View* GetVersionButton() {
return info_view_->GetViewByID(VIEW_ID_QS_VERSION_BUTTON);
}
views::View* GetFeedbackButton() {
return info_view_->GetViewByID(VIEW_ID_QS_FEEDBACK_BUTTON);
}
views::View* GetEolNoticeButton() {
return info_view_->GetViewByID(VIEW_ID_QS_EOL_NOTICE_BUTTON);
}
void TearDown() override {
info_view_ = nullptr;
widget_.reset();
controller_.reset();
model_.reset();
AshTestBase::TearDown();
}
protected:
UnifiedSystemInfoView* info_view() { return info_view_; }
EnterpriseDomainModel* enterprise_domain() {
return Shell::Get()->system_tray_model()->enterprise_domain();
}
private:
scoped_refptr<UnifiedSystemTrayModel> model_;
std::unique_ptr<UnifiedSystemTrayController> controller_;
raw_ptr<UnifiedSystemInfoView, ExperimentalAsh> info_view_ = nullptr;
std::unique_ptr<views::Widget> widget_;
};
INSTANTIATE_TEST_SUITE_P(All,
UnifiedSystemInfoViewTest,
testing::Combine(testing::Bool(), testing::Bool()));
TEST_P(UnifiedSystemInfoViewTest, ButtonNameAndVisibility) {
// By default, EnterpriseManagedView is not shown.
EXPECT_FALSE(GetManagedButton()->GetVisible());
// Simulate enterprise information becoming available.
enterprise_domain()->SetDeviceEnterpriseInfo(
DeviceEnterpriseInfo{"example.com", /*active_directory_managed=*/false,
ManagementDeviceMode::kChromeEnterprise});
// EnterpriseManagedView should be shown.
EXPECT_TRUE(GetManagedButton()->GetVisible());
// `DateView` should be shown.
EXPECT_TRUE(GetDateButton()->GetVisible());
// Battery button should be shown.
EXPECT_TRUE(GetBatteryButton()->GetVisible());
EXPECT_EQ(ShouldShowEolNotice(),
GetEolNoticeButton() && GetEolNoticeButton()->GetVisible());
if (ShouldShowEolNotice()) {
LeftClickOn(GetEolNoticeButton());
EXPECT_EQ(1, GetSystemTrayClient()->show_eol_info_count());
}
const bool show_release_track_info =
IsReleaseTrackNotStable() && !ShouldShowEolNotice();
// If the release track UI is enabled AND the release track is non-stable, the
// version button is shown.
EXPECT_EQ(show_release_track_info,
GetVersionButton() && GetVersionButton()->GetVisible());
// If the release track UI is enabled AND the release track is non-stable AND
// the user feedback is enabled, the feedback button is shown.
EXPECT_EQ(
show_release_track_info &&
Shell::Get()->system_tray_model()->client()->IsUserFeedbackEnabled(),
GetFeedbackButton() && GetFeedbackButton()->GetVisible());
}
TEST_P(UnifiedSystemInfoViewTest, EnterpriseManagedVisibleForActiveDirectory) {
// Active directory information becoming available.
const std::string empty_domain;
enterprise_domain()->SetDeviceEnterpriseInfo(
DeviceEnterpriseInfo{empty_domain, /*active_directory_managed=*/true,
ManagementDeviceMode::kChromeEnterprise});
// EnterpriseManagedView should be shown.
EXPECT_TRUE(GetManagedButton()->GetVisible());
EXPECT_EQ(ShouldShowEolNotice(),
GetEolNoticeButton() && GetEolNoticeButton()->GetVisible());
if (ShouldShowEolNotice()) {
LeftClickOn(GetEolNoticeButton());
EXPECT_EQ(1, GetSystemTrayClient()->show_eol_info_count());
}
const bool show_release_track_info =
IsReleaseTrackNotStable() && !ShouldShowEolNotice();
// If the release track UI is enabled AND the release track is non-stable, the
// version button is shown.
EXPECT_EQ(show_release_track_info,
GetVersionButton() && GetVersionButton()->GetVisible());
// If the release track UI is enabled AND the release track is non-stable AND
// the user feedback is enabled, the feedback button is shown.
EXPECT_EQ(
show_release_track_info &&
Shell::Get()->system_tray_model()->client()->IsUserFeedbackEnabled(),
GetFeedbackButton() && GetFeedbackButton()->GetVisible());
}
TEST_P(UnifiedSystemInfoViewTest, EnterpriseUserManagedVisible) {
// By default, EnterpriseManagedView is not shown.
EXPECT_FALSE(GetManagedButton()->GetVisible());
// Simulate enterprise information becoming available.
enterprise_domain()->SetEnterpriseAccountDomainInfo("example.com");
// EnterpriseManagedView should be shown.
EXPECT_TRUE(GetManagedButton()->GetVisible());
EXPECT_EQ(ShouldShowEolNotice(),
GetEolNoticeButton() && GetEolNoticeButton()->GetVisible());
if (ShouldShowEolNotice()) {
LeftClickOn(GetEolNoticeButton());
EXPECT_EQ(1, GetSystemTrayClient()->show_eol_info_count());
}
const bool show_release_track_info =
IsReleaseTrackNotStable() && !ShouldShowEolNotice();
// If the release track UI is enabled AND the release track is non-stable, the
// version button is shown.
EXPECT_EQ(show_release_track_info,
GetVersionButton() && GetVersionButton()->GetVisible());
// If the release track UI is enabled AND the release track is non-stable AND
// the user feedback is enabled, the feedback button is shown.
EXPECT_EQ(
show_release_track_info &&
Shell::Get()->system_tray_model()->client()->IsUserFeedbackEnabled(),
GetFeedbackButton() && GetFeedbackButton()->GetVisible());
}
TEST_P(UnifiedSystemInfoViewTest, UpdateFiresAccessibilityEvents) {
// Set the current time to 08:00 for testing.
task_environment()->AdvanceClock(base::Time::Now().LocalMidnight() +
base::Hours(32) - base::Time::Now());
views::test::AXEventCounter counter(views::AXEventManager::Get());
auto* date_view = info_view()->GetDateViewForTesting();
auto* date_view_label = info_view()->GetDateViewLabelForTesting();
EXPECT_EQ(0, counter.GetCount(ax::mojom::Event::kTextChanged, date_view));
EXPECT_EQ(0,
counter.GetCount(ax::mojom::Event::kTextChanged, date_view_label));
// `DateView::Update` sets the accessible name of both itself and its label.
// This will result in text-changed events being emitted, but only if the
// accessible name has actually changed. Therefore advance the clock by a
// minute before calling `Update`.
task_environment()->FastForwardBy(base::Minutes(1));
info_view()->UpdateDateViewForTesting();
EXPECT_EQ(1, counter.GetCount(ax::mojom::Event::kTextChanged, date_view));
EXPECT_EQ(1,
counter.GetCount(ax::mojom::Event::kTextChanged, date_view_label));
}
using UnifiedSystemInfoViewNoSessionTest = NoSessionAshTestBase;
TEST_F(UnifiedSystemInfoViewNoSessionTest, ChildVisible) {
auto model = base::MakeRefCounted<UnifiedSystemTrayModel>(nullptr);
auto controller = std::make_unique<UnifiedSystemTrayController>(model.get());
SessionControllerImpl* session = Shell::Get()->session_controller();
ASSERT_FALSE(session->IsActiveUserSessionStarted());
// Before login the supervised user view is invisible.
{
auto info_view = std::make_unique<UnifiedSystemInfoView>(controller.get());
EXPECT_FALSE(info_view->IsSupervisedVisibleForTesting());
}
// Simulate a supervised user logging in.
TestSessionControllerClient* client = GetSessionControllerClient();
client->Reset();
client->AddUserSession("child@test.com", user_manager::USER_TYPE_CHILD);
client->SetSessionState(session_manager::SessionState::ACTIVE);
UserSession user_session = *session->GetUserSession(0);
user_session.custodian_email = "parent@test.com";
session->UpdateUserSession(std::move(user_session));
// Now the supervised user view is visible.
{
auto info_view = std::make_unique<UnifiedSystemInfoView>(controller.get());
EXPECT_TRUE(info_view->IsSupervisedVisibleForTesting());
}
}
} // namespace ash

@ -44,7 +44,6 @@
#include "ash/system/unified/unified_slider_view.h"
#include "ash/system/unified/unified_system_tray_bubble.h"
#include "ash/system/unified/unified_system_tray_model.h"
#include "ash/system/unified/unified_system_tray_view.h"
#include "ash/user_education/user_education_class_properties.h"
#include "ash/user_education/welcome_tour/welcome_tour_metrics.h"
#include "base/memory/raw_ptr.h"
@ -245,30 +244,6 @@ void UnifiedSystemTray::CloseSecondaryBubbles() {
}
}
void UnifiedSystemTray::CollapseMessageCenter() {
}
void UnifiedSystemTray::ExpandMessageCenter() {
}
void UnifiedSystemTray::EnsureQuickSettingsCollapsed(bool animate) {
if (!bubble_) {
return;
}
if (animate) {
bubble_->EnsureCollapsed();
} else {
bubble_->CollapseWithoutAnimating();
}
}
void UnifiedSystemTray::EnsureBubbleExpanded() {
if (bubble_) {
bubble_->EnsureExpanded();
}
}
void UnifiedSystemTray::ShowVolumeSliderBubble() {
slider_bubble_controller_->ShowBubble(
UnifiedSliderBubbleController::SLIDER_TYPE_VOLUME);
@ -306,7 +281,7 @@ void UnifiedSystemTray::ShowNetworkDetailedViewBubble() {
// `bubble_` is still uninitialized). Only show detailed view if `bubble_` is
// not null.
if (bubble_) {
bubble_->ShowNetworkDetailedView(/*force=*/true);
bubble_->ShowNetworkDetailedView();
}
}
@ -331,12 +306,6 @@ bool UnifiedSystemTray::FocusQuickSettings(bool reverse) {
Shell::Get()->focus_cycler()->FocusWidget(quick_settings_widget);
// Focus an individual element in quick settings if chrome vox is
// disabled.
if (!ShouldEnableExtraKeyboardAccessibility()) {
bubble_->FocusEntered(reverse);
}
return true;
}
@ -495,21 +464,12 @@ std::u16string UnifiedSystemTray::GetAccessibleNameForBubble() {
}
std::u16string UnifiedSystemTray::GetAccessibleNameForQuickSettingsBubble() {
if (features::IsQsRevampEnabled()) {
if (bubble_->quick_settings_view()->IsDetailedViewShown()) {
return bubble_->quick_settings_view()->GetDetailedViewAccessibleName();
}
return l10n_util::GetStringUTF16(
IDS_ASH_REVAMPED_QUICK_SETTINGS_BUBBLE_ACCESSIBLE_DESCRIPTION);
}
if (bubble_->unified_view()->IsDetailedViewShown()) {
return bubble_->unified_view()->GetDetailedViewAccessibleName();
}
return l10n_util::GetStringUTF16(
IDS_ASH_QUICK_SETTINGS_BUBBLE_ACCESSIBLE_DESCRIPTION);
IDS_ASH_QUICK_SETTINGS_BUBBLE_ACCESSIBLE_DESCRIPTION);
}
void UnifiedSystemTray::HandleLocaleChange() {

@ -119,18 +119,6 @@ class ASH_EXPORT UnifiedSystemTray
// Activates the system tray bubble.
void ActivateBubble();
// Collapse the message center bubble.
void CollapseMessageCenter();
// Expand the message center bubble.
void ExpandMessageCenter();
// Ensure the quick settings bubble is collapsed.
void EnsureQuickSettingsCollapsed(bool animate);
// Ensure the system tray bubble is expanded.
void EnsureBubbleExpanded();
// Shows volume slider bubble shown at the right bottom of screen. The bubble
// is same as one shown when volume buttons on keyboard are pressed.
void ShowVolumeSliderBubble();

@ -19,7 +19,6 @@
#include "ash/system/unified/quick_settings_metrics_util.h"
#include "ash/system/unified/unified_system_tray.h"
#include "ash/system/unified/unified_system_tray_controller.h"
#include "ash/system/unified/unified_system_tray_view.h"
#include "ash/wm/container_finder.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "base/debug/crash_logging.h"
@ -39,15 +38,12 @@ UnifiedSystemTrayBubble::UnifiedSystemTrayBubble(UnifiedSystemTray* tray)
: controller_(std::make_unique<UnifiedSystemTrayController>(tray->model(),
this,
tray)),
unified_system_tray_(tray),
is_qs_revamp_enabled_(features::IsQsRevampEnabled()) {
unified_system_tray_(tray) {
time_opened_ = base::TimeTicks::Now();
TrayBubbleView::InitParams init_params =
CreateInitParamsForTrayBubble(tray, /*anchor_to_shelf_corner=*/true);
if (is_qs_revamp_enabled_) {
init_params.preferred_width = kRevampedTrayMenuWidth;
}
init_params.preferred_width = kRevampedTrayMenuWidth;
init_params.close_on_deactivate = false;
bubble_view_ = new TrayBubbleView(init_params);
@ -56,23 +52,12 @@ UnifiedSystemTrayBubble::UnifiedSystemTrayBubble(UnifiedSystemTray* tray)
int max_height = CalculateMaxTrayBubbleHeight(
unified_system_tray_->GetBubbleWindowContainer());
if (is_qs_revamp_enabled_) {
auto quick_settings_view = controller_->CreateQuickSettingsView(max_height);
bubble_view_->SetMaxHeight(max_height);
quick_settings_view_ =
bubble_view_->AddChildView(std::move(quick_settings_view));
time_to_click_recorder_ = std::make_unique<TimeToClickRecorder>(
/*delegate=*/this, /*target_view=*/quick_settings_view_);
} else {
DCHECK(!is_qs_revamp_enabled_);
auto unified_view = controller_->CreateUnifiedQuickSettingsView();
unified_view->SetMaxHeight(max_height);
bubble_view_->SetMaxHeight(max_height);
controller_->ResetToCollapsedIfRequired();
unified_view_ = bubble_view_->AddChildView(std::move(unified_view));
time_to_click_recorder_ = std::make_unique<TimeToClickRecorder>(
/*delegate=*/this, /*target_view=*/unified_view_);
}
auto quick_settings_view = controller_->CreateQuickSettingsView(max_height);
bubble_view_->SetMaxHeight(max_height);
quick_settings_view_ =
bubble_view_->AddChildView(std::move(quick_settings_view));
time_to_click_recorder_ = std::make_unique<TimeToClickRecorder>(
/*delegate=*/this, /*target_view=*/quick_settings_view_);
bubble_widget_ = views::BubbleDialogDelegateView::CreateBubble(bubble_view_);
bubble_widget_->AddObserver(this);
@ -91,14 +76,12 @@ UnifiedSystemTrayBubble::UnifiedSystemTrayBubble(UnifiedSystemTray* tray)
UnifiedSystemTrayBubble::~UnifiedSystemTrayBubble() {
// Record the number of quick settings pages.
if (is_qs_revamp_enabled_) {
auto page_count = unified_system_tray_controller()
->model()
->pagination_model()
->total_pages();
DCHECK_GT(page_count, 0);
quick_settings_metrics_util::RecordQsPageCountOnClose(page_count);
}
auto page_count = unified_system_tray_controller()
->model()
->pagination_model()
->total_pages();
DCHECK_GT(page_count, 0);
quick_settings_metrics_util::RecordQsPageCountOnClose(page_count);
if (controller_->showing_calendar_view()) {
unified_system_tray_->NotifyLeavingCalendarView();
@ -115,7 +98,6 @@ UnifiedSystemTrayBubble::~UnifiedSystemTrayBubble() {
if (bubble_view_) {
bubble_view_->RemoveAllChildViews();
quick_settings_view_ = nullptr;
unified_view_ = nullptr;
bubble_view_->ResetDelegate();
bubble_view_ = nullptr;
}
@ -148,57 +130,12 @@ bool UnifiedSystemTrayBubble::IsBubbleActive() const {
return bubble_widget_ && bubble_widget_->IsActive();
}
void UnifiedSystemTrayBubble::EnsureCollapsed() {
if (!bubble_widget_ || is_qs_revamp_enabled_) {
return;
}
DCHECK(unified_view_);
DCHECK(controller_);
controller_->EnsureCollapsed();
}
void UnifiedSystemTrayBubble::EnsureExpanded() {
if (!bubble_widget_) {
return;
}
DCHECK(unified_view_ || quick_settings_view_);
DCHECK(controller_);
controller_->EnsureExpanded();
}
void UnifiedSystemTrayBubble::CollapseWithoutAnimating() {
if (!bubble_widget_ || is_qs_revamp_enabled_) {
return;
}
DCHECK(unified_view_);
DCHECK(controller_);
controller_->CollapseWithoutAnimating();
}
void UnifiedSystemTrayBubble::CollapseMessageCenter() {
if (is_qs_revamp_enabled_) {
return;
}
unified_system_tray_->CollapseMessageCenter();
}
void UnifiedSystemTrayBubble::ExpandMessageCenter() {
if (is_qs_revamp_enabled_) {
return;
}
unified_system_tray_->ExpandMessageCenter();
}
void UnifiedSystemTrayBubble::ShowAudioDetailedView() {
if (!bubble_widget_) {
return;
}
DCHECK(unified_view_ || quick_settings_view_);
DCHECK(quick_settings_view_);
DCHECK(controller_);
controller_->ShowAudioDetailedView();
}
@ -208,7 +145,7 @@ void UnifiedSystemTrayBubble::ShowDisplayDetailedView() {
return;
}
DCHECK(unified_view_ || quick_settings_view_);
DCHECK(quick_settings_view_);
DCHECK(controller_);
controller_->ShowDisplayDetailedView();
}
@ -231,19 +168,19 @@ void UnifiedSystemTrayBubble::ShowCalendarView(
}
}
DCHECK(unified_view_ || quick_settings_view_);
DCHECK(quick_settings_view_);
DCHECK(controller_);
controller_->ShowCalendarView(show_source, event_source);
}
void UnifiedSystemTrayBubble::ShowNetworkDetailedView(bool force) {
void UnifiedSystemTrayBubble::ShowNetworkDetailedView() {
if (!bubble_widget_) {
return;
}
DCHECK(unified_view_ || quick_settings_view_);
DCHECK(quick_settings_view_);
DCHECK(controller_);
controller_->ShowNetworkDetailedView(force);
controller_->ShowNetworkDetailedView();
}
void UnifiedSystemTrayBubble::UpdateBubble() {
@ -268,37 +205,12 @@ views::Widget* UnifiedSystemTrayBubble::GetBubbleWidget() const {
}
int UnifiedSystemTrayBubble::GetCurrentTrayHeight() const {
if (is_qs_revamp_enabled_) {
CHECK(quick_settings_view_);
return quick_settings_view_->GetCurrentHeight();
}
return unified_view_->GetCurrentHeight();
return quick_settings_view_->GetCurrentHeight();
}
bool UnifiedSystemTrayBubble::FocusOut(bool reverse) {
if (is_qs_revamp_enabled_) {
return false;
}
return unified_system_tray_->FocusMessageCenter(reverse);
}
void UnifiedSystemTrayBubble::FocusEntered(bool reverse) {
if (is_qs_revamp_enabled_) {
return;
}
unified_view_->FocusEntered(reverse);
}
void UnifiedSystemTrayBubble::OnMessageCenterActivated() {
if (is_qs_revamp_enabled_) {
return;
}
// When the message center is activated, we no longer need to reroute key
// events to this bubble. Otherwise, we interfere with notifications that may
// require key input like inline replies. See crbug.com/1040738.
bubble_view_->StopReroutingEvents();
// TODO(b/309529593) Remove this method and clean up the related code.
return false;
}
void UnifiedSystemTrayBubble::OnDisplayConfigurationChanged() {
@ -312,7 +224,6 @@ void UnifiedSystemTrayBubble::OnWidgetDestroying(views::Widget* widget) {
bubble_view_->RemoveAllChildViews();
quick_settings_view_ = nullptr;
unified_view_ = nullptr;
bubble_view_->ResetDelegate();
bubble_view_ = nullptr;
@ -345,7 +256,6 @@ void UnifiedSystemTrayBubble::OnAutoHideStateChanged(
}
void UnifiedSystemTrayBubble::UpdateBubbleHeight(bool is_showing_detiled_view) {
DCHECK(is_qs_revamp_enabled_);
if (!bubble_view_) {
return;
}
@ -368,8 +278,7 @@ void UnifiedSystemTrayBubble::UpdateBubbleBounds() {
!!bubble_widget_ && !!bubble_widget_->IsClosed());
// `bubble_view_` or `Shelf` may be null, see https://b/293264371,
if (!bubble_view_ || (is_qs_revamp_enabled_ && !quick_settings_view_) ||
(!is_qs_revamp_enabled_ && !unified_view_)) {
if (!bubble_view_ || !quick_settings_view_) {
return;
}
if (!unified_system_tray_->shelf()) {
@ -379,18 +288,15 @@ void UnifiedSystemTrayBubble::UpdateBubbleBounds() {
int max_height = CalculateMaxTrayBubbleHeight(
unified_system_tray_->GetBubbleWindowContainer());
if (bubble_view_->ShouldUseFixedHeight()) {
DCHECK(is_qs_revamp_enabled_);
const int qs_current_height = quick_settings_view_->height();
max_height =
std::min(max_height, std::max(qs_current_height, kDetailedViewHeight));
}
if (is_qs_revamp_enabled_) {
// Setting the max height can result in the popup baseline being updated,
// closing this bubble.
quick_settings_view_->SetMaxHeight(max_height);
} else {
unified_view_->SetMaxHeight(max_height);
}
// Setting the max height can result in the popup baseline being updated,
// closing this bubble.
quick_settings_view_->SetMaxHeight(max_height);
if (!bubble_view_) {
// Updating the maximum height can result in popup baseline changing. If
// there is not enough room for popups, the bubble will be closed, and this

@ -34,10 +34,9 @@ namespace ash {
class TrayEventFilter;
class UnifiedSystemTray;
class UnifiedSystemTrayController;
class UnifiedSystemTrayView;
class QuickSettingsView;
// Manages the bubble that contains UnifiedSystemTrayView.
// Manages the bubble that contains 'QuickSettingsView'.
// Shows the bubble on the constructor, and closes the bubble on the destructor.
// It is possible that the bubble widget is closed on deactivation. In such
// case, this class calls UnifiedSystemTray::CloseBubble() to delete itself.
@ -65,21 +64,6 @@ class ASH_EXPORT UnifiedSystemTrayBubble : public TrayBubbleBase,
// True if the bubble is active.
bool IsBubbleActive() const;
// Collapse the message center bubble.
void CollapseMessageCenter();
// Expand the message center bubble.
void ExpandMessageCenter();
// Ensure the bubble is collapsed.
void EnsureCollapsed();
// Ensure the bubble is expanded.
void EnsureExpanded();
// Set the state to collapsed without animation.
void CollapseWithoutAnimating();
// Show audio settings detailed view.
void ShowAudioDetailedView();
@ -91,7 +75,7 @@ class ASH_EXPORT UnifiedSystemTrayBubble : public TrayBubbleBase,
calendar_metrics::CalendarEventSource event_source);
// Show network settings detailed view.
void ShowNetworkDetailedView(bool force);
void ShowNetworkDetailedView();
// Update bubble bounds and focus if necessary.
void UpdateBubble();
@ -103,12 +87,6 @@ class ASH_EXPORT UnifiedSystemTrayBubble : public TrayBubbleBase,
// Relinquish focus and transfer it to the message center widget.
bool FocusOut(bool reverse);
// Inform UnifiedSystemTrayView of focus being acquired.
void FocusEntered(bool reverse);
// Called when the message center widget is activated.
void OnMessageCenterActivated();
// Fire a notification that an accessibility event has occured on this object.
void NotifyAccessibilityEvent(ax::mojom::Event event, bool send_native_event);
@ -142,8 +120,6 @@ class ASH_EXPORT UnifiedSystemTrayBubble : public TrayBubbleBase,
// the detailed page.
void UpdateBubbleHeight(bool is_showing_detiled_view);
UnifiedSystemTrayView* unified_view() { return unified_view_; }
QuickSettingsView* quick_settings_view() { return quick_settings_view_; }
UnifiedSystemTrayController* unified_system_tray_controller() {
@ -155,33 +131,30 @@ class ASH_EXPORT UnifiedSystemTrayBubble : public TrayBubbleBase,
void UpdateBubbleBounds();
// Controller of UnifiedSystemTrayView. As the view is owned by views
// hierarchy, we have to own the controller here.
// Controller of `QuickSettingsView`. As the view is owned by views hierarchy,
// we have to own the controller here.
std::unique_ptr<UnifiedSystemTrayController> controller_;
// Owner of this class.
raw_ptr<UnifiedSystemTray, ExperimentalAsh> unified_system_tray_;
// Widget that contains `UnifiedSystemTrayView`. Unowned.
// Widget that contains `QuickSettingsView`. Unowned.
// When the widget is closed by deactivation, `bubble_widget_` pointer is
// invalidated and we have to delete `UnifiedSystemTrayBubble` by calling
// `UnifiedSystemTray::CloseBubble()`.
// In order to do this, we observe `OnWidgetDestroying()`.
raw_ptr<views::Widget, ExperimentalAsh> bubble_widget_ = nullptr;
// PreTargetHandler of |unified_view_| to record TimeToClick metrics. Owned.
// PreTargetHandler of `quick_settings_view_` to record TimeToClick metrics.
// Owned.
std::unique_ptr<TimeToClickRecorder> time_to_click_recorder_;
// The time the bubble is created.
absl::optional<base::TimeTicks> time_opened_;
raw_ptr<TrayBubbleView, ExperimentalAsh> bubble_view_ = nullptr;
raw_ptr<UnifiedSystemTrayView, DanglingUntriaged> unified_view_ = nullptr;
// Only non-null when QsRevamp is enabled.
raw_ptr<QuickSettingsView, DanglingUntriaged> quick_settings_view_ = nullptr;
// Whether the QsRevamp feature is enabled.
const bool is_qs_revamp_enabled_;
raw_ptr<QuickSettingsView, DanglingUntriaged> quick_settings_view_ = nullptr;
std::unique_ptr<TrayEventFilter> tray_event_filter_;

@ -57,9 +57,7 @@
#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_button.h"
#include "ash/system/unified/feature_pod_controller_base.h"
#include "ash/system/unified/feature_pods_container_view.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"
@ -68,7 +66,6 @@
#include "ash/system/unified/unified_notifier_settings_controller.h"
#include "ash/system/unified/unified_system_tray_bubble.h"
#include "ash/system/unified/unified_system_tray_model.h"
#include "ash/system/unified/unified_system_tray_view.h"
#include "ash/system/unified/user_chooser_detailed_view_controller.h"
#include "ash/wm/lock_state_controller.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
@ -83,8 +80,6 @@
#include "ui/compositor/compositor.h"
#include "ui/display/screen.h"
#include "ui/events/event.h"
#include "ui/gfx/animation/slide_animation.h"
#include "ui/message_center/message_center.h"
#include "ui/views/widget/widget.h"
namespace ash {
@ -110,22 +105,12 @@ UnifiedSystemTrayController::UnifiedSystemTrayController(
scoped_refptr<UnifiedSystemTrayModel> model,
UnifiedSystemTrayBubble* bubble,
views::View* owner_view)
: views::AnimationDelegateViews(owner_view),
model_(model),
: model_(model),
bubble_(bubble),
active_user_prefs_(
Shell::Get()->session_controller()->GetLastActiveUserPrefService()),
animation_(std::make_unique<gfx::SlideAnimation>(this)) {
Shell::Get()->session_controller()->GetLastActiveUserPrefService()) {
LoadIsExpandedPref();
const float animation_value = features::IsQsRevampEnabled()
? 1
: (model_->IsExpandedOnOpen() ? 1.0 : 0.0);
animation_->Reset(animation_value);
animation_->SetSlideDuration(
base::Milliseconds(kSystemMenuCollapseExpandAnimationDurationMs));
animation_->SetTweenType(gfx::Tween::EASE_IN_OUT);
model_->pagination_model()->SetTransitionDurations(base::Milliseconds(250),
base::Milliseconds(50));
@ -165,37 +150,6 @@ void UnifiedSystemTrayController::OnActiveUserPrefServiceChanged(
active_user_prefs_ = prefs;
}
std::unique_ptr<UnifiedSystemTrayView>
UnifiedSystemTrayController::CreateUnifiedQuickSettingsView() {
DCHECK(!unified_view_);
auto unified_view =
std::make_unique<UnifiedSystemTrayView>(this, model_->IsExpandedOnOpen());
unified_view_ = unified_view.get();
InitFeaturePods();
if (!Shell::Get()->session_controller()->IsScreenLocked() &&
!MediaTray::IsPinnedToShelf()) {
media_controls_controller_ =
std::make_unique<UnifiedMediaControlsController>(this);
unified_view->AddMediaControlsView(
media_controls_controller_->CreateView());
}
volume_slider_controller_ =
std::make_unique<UnifiedVolumeSliderController>(this);
unified_view->AddSliderView(volume_slider_controller_->CreateView());
brightness_slider_controller_ =
std::make_unique<UnifiedBrightnessSliderController>(
model_, views::Button::PressedCallback(base::BindRepeating(
&UnifiedSystemTrayController::ShowDisplayDetailedView,
base::Unretained(this))));
unified_view->AddSliderView(brightness_slider_controller_->CreateView());
return unified_view;
}
std::unique_ptr<QuickSettingsView>
UnifiedSystemTrayController::CreateQuickSettingsView(int max_height) {
DCHECK(!quick_settings_view_);
@ -261,17 +215,10 @@ void UnifiedSystemTrayController::HandleLockAction() {
void UnifiedSystemTrayController::HandleSettingsAction() {
base::RecordAction(base::UserMetricsAction("Tray_Settings"));
if (features::IsQsRevampEnabled()) {
Shell::Get()->system_tray_model()->client()->ShowSettings(
display::Screen::GetScreen()
->GetDisplayNearestView(
quick_settings_view_->GetWidget()->GetNativeView())
.id());
return;
}
Shell::Get()->system_tray_model()->client()->ShowSettings(
display::Screen::GetScreen()
->GetDisplayNearestView(unified_view_->GetWidget()->GetNativeView())
->GetDisplayNearestView(
quick_settings_view_->GetWidget()->GetNativeView())
.id());
}
@ -318,177 +265,23 @@ void UnifiedSystemTrayController::HandleEnterpriseInfoAction() {
Shell::Get()->system_tray_model()->client()->ShowEnterpriseInfo();
}
void UnifiedSystemTrayController::ToggleExpanded() {
if (features::IsQsRevampEnabled()) {
return;
}
if (animation_->is_animating()) {
return;
}
UMA_HISTOGRAM_ENUMERATION("ChromeOS.SystemTray.ToggleExpanded",
TOGGLE_EXPANDED_TYPE_BY_BUTTON,
TOGGLE_EXPANDED_TYPE_COUNT);
if (IsExpanded()) {
StartAnimation(/*expand=*/false);
// Expand message center when quick settings is collapsed.
if (bubble_) {
bubble_->ExpandMessageCenter();
}
} else {
// Collapse the message center if screen height is limited after expanding
// the quick settings to its full height.
if (IsMessageCenterCollapseRequired()) {
bubble_->CollapseMessageCenter();
}
StartAnimation(/*expand=*/true);
}
}
void UnifiedSystemTrayController::BeginDrag(const gfx::PointF& location) {
if (features::IsQsRevampEnabled()) {
return;
}
UpdateDragThreshold();
// Ignore swipe collapsing when a detailed view is shown as it's confusing.
if (detailed_view_controller_) {
return;
}
drag_init_point_ = location;
was_expanded_ = IsExpanded();
}
void UnifiedSystemTrayController::UpdateDrag(const gfx::PointF& location) {
if (features::IsQsRevampEnabled()) {
return;
}
// Ignore swipe collapsing when a detailed view is shown as it's confusing.
if (detailed_view_controller_) {
return;
}
double drag_expanded_amount = GetDragExpandedAmount(location);
animation_->Reset(drag_expanded_amount);
UpdateExpandedAmount();
if (was_expanded_ &&
drag_expanded_amount < kNotificationCenterDragExpandThreshold) {
bubble_->ExpandMessageCenter();
} else if (drag_expanded_amount >= kNotificationCenterDragExpandThreshold &&
IsMessageCenterCollapseRequired()) {
bubble_->CollapseMessageCenter();
}
}
void UnifiedSystemTrayController::StartAnimation(bool expand) {
// UnifiedSystemTrayControllerTest does not add `unified_view_` to a widget.
if (features::IsQsRevampEnabled()) {
return;
}
if (unified_view_->GetWidget()) {
animation_tracker_.emplace(unified_view_->GetWidget()
->GetCompositor()
->RequestNewThroughputTracker());
animation_tracker_->Start(metrics_util::ForSmoothness(
expand ? base::BindRepeating(&ReportExpandAnimationSmoothness)
: base::BindRepeating(&ReportCollapseAnimationSmoothness)));
}
if (expand) {
animation_->Show();
} else {
// To animate to hidden state, first set SlideAnimation::IsShowing() to
// true.
animation_->Show();
animation_->Hide();
}
}
void UnifiedSystemTrayController::EndDrag(const gfx::PointF& location) {
if (features::IsQsRevampEnabled()) {
return;
}
// Ignore swipe collapsing when a detailed view is shown as it's confusing.
if (detailed_view_controller_) {
return;
}
if (animation_->is_animating()) {
// Prevent overwriting the state right after fling event
return;
}
bool expanded = GetDragExpandedAmount(location) > 0.5;
if (was_expanded_ != expanded) {
UMA_HISTOGRAM_ENUMERATION("ChromeOS.SystemTray.ToggleExpanded",
TOGGLE_EXPANDED_TYPE_BY_GESTURE,
TOGGLE_EXPANDED_TYPE_COUNT);
}
if (expanded && IsMessageCenterCollapseRequired()) {
bubble_->CollapseMessageCenter();
} else {
bubble_->ExpandMessageCenter();
}
// If dragging is finished, animate to closer state.
StartAnimation(expanded);
}
void UnifiedSystemTrayController::Fling(int velocity) {
if (features::IsQsRevampEnabled()) {
return;
}
// Ignore swipe collapsing when a detailed view is shown as it's confusing.
if (detailed_view_controller_) {
return;
}
// Expand when flinging up. Collapse otherwise.
bool expand = (velocity < 0);
if (expand && IsMessageCenterCollapseRequired()) {
bubble_->CollapseMessageCenter();
} else {
bubble_->ExpandMessageCenter();
}
StartAnimation(expand);
}
void UnifiedSystemTrayController::ShowUserChooserView() {
if (!UserChooserDetailedViewController::IsUserChooserEnabled()) {
return;
}
animation_->Reset(1.0);
UpdateExpandedAmount();
ShowDetailedView(std::make_unique<UserChooserDetailedViewController>(this));
}
void UnifiedSystemTrayController::ShowNetworkDetailedView(bool force) {
if (!force && !IsExpanded()) {
return;
}
void UnifiedSystemTrayController::ShowNetworkDetailedView() {
base::RecordAction(base::UserMetricsAction("StatusArea_Network_Detailed"));
ShowDetailedView(std::make_unique<NetworkDetailedViewController>(this));
}
void UnifiedSystemTrayController::ShowHotspotDetailedView() {
DCHECK(features::IsQsRevampEnabled());
ShowDetailedView(std::make_unique<HotspotDetailedViewController>(this));
}
void UnifiedSystemTrayController::ShowBluetoothDetailedView() {
// QSRevamp does not allow expand/collapse of the System Tray.
if (!IsExpanded() && !features::IsQsRevampEnabled()) {
return;
}
base::RecordAction(base::UserMetricsAction("StatusArea_Bluetooth_Detailed"));
ShowDetailedView(std::make_unique<BluetoothDetailedViewController>(this));
}
@ -582,30 +375,17 @@ void UnifiedSystemTrayController::TransitionToMainView(bool restore_focus) {
// reference to its `detailed_view_controller_` which is used in shutdown.
auto scoped_detailed_view_controller = std::move(detailed_view_controller_);
if (features::IsQsRevampEnabled()) {
bubble_->UpdateBubbleHeight(/*is_showing_detiled_view=*/false);
quick_settings_view_->ResetDetailedView();
if (restore_focus) {
quick_settings_view_->RestoreFocus();
}
UpdateBubble();
return;
}
unified_view_->ResetDetailedView();
bubble_->UpdateBubbleHeight(/*is_showing_detiled_view=*/false);
quick_settings_view_->ResetDetailedView();
if (restore_focus) {
unified_view_->RestoreFocus();
quick_settings_view_->RestoreFocus();
}
UpdateBubble();
}
void UnifiedSystemTrayController::CloseBubble() {
if (features::IsQsRevampEnabled()) {
if (quick_settings_view_->GetWidget()) {
quick_settings_view_->GetWidget()->CloseNow();
}
return;
}
if (unified_view_->GetWidget()) {
unified_view_->GetWidget()->CloseNow();
if (quick_settings_view_->GetWidget()) {
quick_settings_view_->GetWidget()->CloseNow();
}
}
@ -613,71 +393,12 @@ bool UnifiedSystemTrayController::FocusOut(bool reverse) {
return bubble_->FocusOut(reverse);
}
void UnifiedSystemTrayController::EnsureCollapsed() {
if (features::IsQsRevampEnabled()) {
return;
}
if (IsExpanded()) {
animation_->Hide();
}
}
void UnifiedSystemTrayController::EnsureExpanded() {
if (detailed_view_controller_) {
// If a detailed view is showing, first transit to the main view.
TransitionToMainView(false);
}
StartAnimation(true /*expand*/);
if (IsMessageCenterCollapseRequired()) {
bubble_->CollapseMessageCenter();
}
}
void UnifiedSystemTrayController::AnimationEnded(
const gfx::Animation* animation) {
if (features::IsQsRevampEnabled()) {
return;
}
if (animation_tracker_) {
animation_tracker_->Stop();
animation_tracker_.reset();
}
UpdateExpandedAmount();
}
void UnifiedSystemTrayController::AnimationProgressed(
const gfx::Animation* animation) {
if (features::IsQsRevampEnabled()) {
return;
}
UpdateExpandedAmount();
}
void UnifiedSystemTrayController::AnimationCanceled(
const gfx::Animation* animation) {
if (features::IsQsRevampEnabled()) {
return;
}
animation_->Reset(std::round(animation_->GetCurrentValue()));
UpdateExpandedAmount();
}
void UnifiedSystemTrayController::OnAudioSettingsButtonClicked() {
ShowAudioDetailedView();
}
void UnifiedSystemTrayController::ShowMediaControls() {
if (features::IsQsRevampEnabled()) {
quick_settings_view_->ShowMediaControls();
return;
}
unified_view_->ShowMediaControls();
quick_settings_view_->ShowMediaControls();
}
void UnifiedSystemTrayController::OnMediaControlsViewClicked() {
@ -698,10 +419,6 @@ void UnifiedSystemTrayController::LoadIsExpandedPref() {
}
}
void UnifiedSystemTrayController::InitFeaturePods() {
// TODO(b/251724646): remove with `unified_view_`.
}
void UnifiedSystemTrayController::InitFeatureTiles() {
std::vector<std::unique_ptr<FeatureTile>> tiles;
@ -773,23 +490,11 @@ void UnifiedSystemTrayController::InitFeatureTiles() {
Shell::Get()->tablet_mode_controller()->InTabletMode());
}
void UnifiedSystemTrayController::AddFeaturePodItem(
std::unique_ptr<FeaturePodControllerBase> controller) {
// TODO(b/251724646): remove with `unified_view_`.
}
void UnifiedSystemTrayController::ShowDetailedView(
std::unique_ptr<DetailedViewController> controller) {
animation_->Reset(1.0);
UpdateExpandedAmount();
views::FocusManager* manager;
if (features::IsQsRevampEnabled()) {
quick_settings_view_->SaveFocus();
manager = quick_settings_view_->GetFocusManager();
} else {
unified_view_->SaveFocus();
manager = unified_view_->GetFocusManager();
}
quick_settings_view_->SaveFocus();
manager = quick_settings_view_->GetFocusManager();
if (manager && manager->GetFocusedView()) {
manager->ClearFocus();
@ -797,12 +502,8 @@ void UnifiedSystemTrayController::ShowDetailedView(
showing_audio_detailed_view_ = false;
showing_display_detailed_view_ = false;
if (features::IsQsRevampEnabled()) {
bubble_->UpdateBubbleHeight(/*is_showing_detiled_view=*/true);
quick_settings_view_->SetDetailedView(controller->CreateView());
} else {
unified_view_->SetDetailedView(controller->CreateView());
}
bubble_->UpdateBubbleHeight(/*is_showing_detiled_view=*/true);
quick_settings_view_->SetDetailedView(controller->CreateView());
detailed_view_controller_ = std::move(controller);
@ -814,83 +515,13 @@ void UnifiedSystemTrayController::ShowDetailedView(
}
}
void UnifiedSystemTrayController::UpdateExpandedAmount() {
if (quick_settings_view_) {
return;
}
double expanded_amount = animation_->GetCurrentValue();
unified_view_->SetExpandedAmount(expanded_amount);
if (expanded_amount == 0.0 || expanded_amount == 1.0) {
model_->set_expanded_on_open(
expanded_amount == 1.0
? UnifiedSystemTrayModel::StateOnOpen::EXPANDED
: UnifiedSystemTrayModel::StateOnOpen::COLLAPSED);
}
}
void UnifiedSystemTrayController::ResetToCollapsedIfRequired() {
if (quick_settings_view_) {
return;
}
if (model_->IsExplicitlyExpanded()) {
return;
}
if (unified_view_->feature_pods_container()->row_count() ==
kUnifiedFeaturePodMinRows) {
CollapseWithoutAnimating();
}
}
void UnifiedSystemTrayController::CollapseWithoutAnimating() {
if (features::IsQsRevampEnabled()) {
return;
}
unified_view_->SetExpandedAmount(0.0);
animation_->Reset(0);
}
bool UnifiedSystemTrayController::IsDetailedViewShown() const {
if (quick_settings_view_) {
return quick_settings_view_->IsDetailedViewShown();
}
if (unified_view_) {
return unified_view_->IsDetailedViewShown();
}
return false;
}
void UnifiedSystemTrayController::UpdateDragThreshold() {
if (features::IsQsRevampEnabled()) {
return;
}
UnifiedSystemTrayView* unified_view = bubble_->unified_view();
drag_threshold_ = unified_view->GetExpandedSystemTrayHeight() -
unified_view->GetCollapsedSystemTrayHeight();
}
double UnifiedSystemTrayController::GetDragExpandedAmount(
const gfx::PointF& location) const {
if (features::IsQsRevampEnabled()) {
return 1.0;
}
double y_diff = (location - drag_init_point_).y();
// If already expanded, only consider swiping down. Otherwise, only consider
// swiping up.
if (was_expanded_) {
return std::clamp(1.0 - std::max(0.0, y_diff) / drag_threshold_, 0.0, 1.0);
} else {
return std::clamp(std::max(0.0, -y_diff) / drag_threshold_, 0.0, 1.0);
}
}
bool UnifiedSystemTrayController::IsExpanded() const {
return features::IsQsRevampEnabled() || animation_->IsShowing();
}
void UnifiedSystemTrayController::UpdateBubble() {
if (!bubble_) {
return;
@ -898,29 +529,6 @@ void UnifiedSystemTrayController::UpdateBubble() {
bubble_->UpdateBubble();
}
bool UnifiedSystemTrayController::IsMessageCenterCollapseRequired() const {
if (quick_settings_view_) {
return false;
}
if (!bubble_) {
return false;
}
// Note: This calculaton should be the same as
// UnifiedMessageCenterBubble::CalculateAvailableHeight().
auto available_height = CalculateMaxTrayBubbleHeight(
bubble_->GetTray()->GetBubbleWindowContainer());
available_height -= unified_view_->GetExpandedSystemTrayHeight();
available_height -= kUnifiedMessageCenterBubbleSpacing;
return available_height < kMessageCenterCollapseThreshold;
}
base::TimeDelta UnifiedSystemTrayController::GetAnimationDurationForReporting()
const {
return base::Milliseconds(kSystemMenuCollapseExpandAnimationDurationMs);
}
bool UnifiedSystemTrayController::ShouldShowDeferredUpdateDialog() const {
return Shell::Get()->system_tray_model()->update_model()->update_deferred() ==
DeferredUpdateState::kShowDialog;

@ -13,23 +13,14 @@
#include "ash/system/audio/unified_volume_slider_controller.h"
#include "ash/system/media/unified_media_controls_controller.h"
#include "ash/system/time/calendar_metrics.h"
#include "ash/system/time/calendar_model.h"
#include "ash/system/unified/quick_settings_view.h"
#include "ash/system/unified/unified_system_tray_model.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/compositor/throughput_tracker.h"
#include "ui/gfx/geometry/point.h"
#include "ui/views/animation/animation_delegate_views.h"
class PrefRegistrySimple;
class PrefService;
namespace gfx {
class SlideAnimation;
} // namespace gfx
namespace views {
class View;
} // namespace views
@ -45,12 +36,10 @@ class UnifiedBrightnessSliderController;
class UnifiedVolumeSliderController;
class UnifiedSystemTrayBubble;
class UnifiedSystemTrayModel;
class UnifiedSystemTrayView;
// Controller class of UnifiedSystemTrayView. Handles events of the view.
// Controller class of `QuickSettingsView`. Handles events of the view.
class ASH_EXPORT UnifiedSystemTrayController
: public views::AnimationDelegateViews,
public SessionObserver,
: public SessionObserver,
public UnifiedVolumeSliderController::Delegate,
public UnifiedMediaControlsController::Delegate {
public:
@ -81,7 +70,6 @@ class ASH_EXPORT UnifiedSystemTrayController
static void RegisterProfilePrefs(PrefRegistrySimple* registry);
// Create the view in the bubble.
std::unique_ptr<UnifiedSystemTrayView> CreateUnifiedQuickSettingsView();
std::unique_ptr<QuickSettingsView> CreateQuickSettingsView(int max_height);
// Sign out from the current user. Called from the view.
@ -100,20 +88,11 @@ class ASH_EXPORT UnifiedSystemTrayController
void HandleOpenPowerSettingsAction();
// Show enterprise managed device info. Called from the view.
void HandleEnterpriseInfoAction();
// Toggle expanded state of UnifiedSystemTrayView. Called from the view.
void ToggleExpanded();
// Handle finger dragging and expand/collapse the view. Called from view.
void BeginDrag(const gfx::PointF& location);
void UpdateDrag(const gfx::PointF& location);
void EndDrag(const gfx::PointF& location);
void Fling(int velocity);
// Show user selector view. Called from the view.
void ShowUserChooserView();
// Show the detailed view of network. If |force| is true, it shows the
// detailed view even if it's collapsed. Called from the view.
void ShowNetworkDetailedView(bool force);
// Show the detailed view of network. Called from the view.
void ShowNetworkDetailedView();
// Show the detailed view of hotspot. Called from the view.
void ShowHotspotDetailedView();
// Show the detailed view of bluetooth. If collapsed, it doesn't show the
@ -154,35 +133,16 @@ class ASH_EXPORT UnifiedSystemTrayController
// Close the bubble. Called from a detailed view controller.
void CloseBubble();
// Inform UnifiedSystemTrayBubble that UnifiedSystemTrayView is requesting to
// Inform `UnifiedSystemTrayBubble` that `QuickSettingsView` is requesting to
// relinquish focus.
bool FocusOut(bool reverse);
// Ensure the main view is collapsed. Called from the slider bubble
// controller.
void EnsureCollapsed();
// Ensure the main view is expanded. Called from the slider bubble controller.
void EnsureExpanded();
// Collapse the tray without animating if there isn't sufficient space for the
// notifications area.
void ResetToCollapsedIfRequired();
// Collapse the tray without animating.
void CollapseWithoutAnimating();
// Return whether a detailed view is currently being shown.
bool IsDetailedViewShown() const;
// SessionObserver:
void OnActiveUserPrefServiceChanged(PrefService* pref_service) override;
// views::AnimationDelegateViews:
void AnimationEnded(const gfx::Animation* animation) override;
void AnimationProgressed(const gfx::Animation* animation) override;
void AnimationCanceled(const gfx::Animation* animation) override;
// UnifiedVolumeSliderController::Delegate:
void OnAudioSettingsButtonClicked() override;
@ -193,9 +153,6 @@ class ASH_EXPORT UnifiedSystemTrayController
// Sets whether the quick settings view should show the media view.
void SetShowMediaView(bool show_media_view);
// Return true if UnifiedSystemTray is expanded.
bool IsExpanded() const;
// Update the bubble view layout.
void UpdateBubble();
@ -231,14 +188,6 @@ class ASH_EXPORT UnifiedSystemTrayController
friend class UnifiedSystemTrayControllerTest;
friend class UnifiedVolumeViewTest;
// How the expanded state is toggled. The enum is used to back an UMA
// histogram and should be treated as append-only.
enum ToggleExpandedType {
TOGGLE_EXPANDED_TYPE_BY_BUTTON = 0,
TOGGLE_EXPANDED_TYPE_BY_GESTURE,
TOGGLE_EXPANDED_TYPE_COUNT
};
// Type of a help page opened by the "Managed" indicator in the bubble. The
// enum is used to back an UMA histogram and should be treated as append-only.
enum ManagedType { MANAGED_TYPE_ENTERPRISE = 0, MANAGED_TYPE_COUNT };
@ -246,51 +195,18 @@ class ASH_EXPORT UnifiedSystemTrayController
// Loads the `kSystemTrayExpanded` pref to the model.
void LoadIsExpandedPref();
// Initialize feature pod controllers and their views.
// If you want to add a new feature pod item, you have to add here.
void InitFeaturePods();
// Initialize feature pod controllers and their feature tile views.
void InitFeatureTiles();
// Add the feature pod controller and its view.
void AddFeaturePodItem(std::unique_ptr<FeaturePodControllerBase> controller);
// Show the detailed view.
void ShowDetailedView(std::unique_ptr<DetailedViewController> controller);
// Update how much the view is expanded based on |animation_|.
void UpdateExpandedAmount();
// Update the gesture distance by using the tray's collapsed and expanded
// height.
void UpdateDragThreshold();
// Return touch drag amount between 0.0 and 1.0. If expanding, it increases
// towards 1.0. If collapsing, it decreases towards 0.0. If the view is
// dragged to the same direction as the current state, it does not change the
// value. For example, if the view is expanded and it's dragged to the top, it
// keeps returning 1.0.
double GetDragExpandedAmount(const gfx::PointF& location) const;
// Return true if message center needs to be collapsed due to limited
// screen height.
bool IsMessageCenterCollapseRequired() const;
// Starts animation to expand or collapse the bubble.
void StartAnimation(bool expand);
// views::AnimationDelegateViews:
base::TimeDelta GetAnimationDurationForReporting() const override;
bool ShouldShowDeferredUpdateDialog() const;
// Model that stores UI specific variables. Unowned.
scoped_refptr<UnifiedSystemTrayModel> model_;
// Unowned. Owned by Views hierarchy.
raw_ptr<UnifiedSystemTrayView, DanglingUntriaged | ExperimentalAsh>
unified_view_ = nullptr;
raw_ptr<QuickSettingsView, DanglingUntriaged | ExperimentalAsh>
quick_settings_view_ = nullptr;
@ -324,24 +240,6 @@ class ASH_EXPORT UnifiedSystemTrayController
raw_ptr<views::View, DanglingUntriaged | ExperimentalAsh>
unified_brightness_view_ = nullptr;
// If the previous state is expanded or not. Only valid during dragging (from
// BeginDrag to EndDrag).
bool was_expanded_ = true;
// The last |location| passed to BeginDrag(). Only valid during dragging.
gfx::PointF drag_init_point_;
// Threshold in pixel that fully collapses / expands the view through gesture.
// Used to calculate the expanded amount that corresponds to gesture location
// during drag.
double drag_threshold_ = 0;
// Animation between expanded and collapsed states.
std::unique_ptr<gfx::SlideAnimation> animation_;
// Tracks the smoothness of collapse and expand animation.
absl::optional<ui::ThroughputTracker> animation_tracker_;
bool showing_audio_detailed_view_ = false;
bool showing_display_detailed_view_ = false;

@ -5,29 +5,26 @@
#include "ash/system/unified/unified_system_tray_controller.h"
#include <memory>
#include "ash/constants/ash_features.h"
#include "ash/system/brightness/unified_brightness_slider_controller.h"
#include "ash/system/status_area_widget.h"
#include "ash/system/unified/unified_system_tray_model.h"
#include "ash/test/ash_test_base.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/test/scoped_feature_list.h"
#include "chromeos/ash/services/network_config/public/cpp/cros_network_config_test_helper.h"
#include "ui/views/test/views_test_utils.h"
#include "ui/views/view_observer.h"
namespace ash {
class QsRevampUnifiedSystemTrayControllerTest : public AshTestBase {
class UnifiedSystemTrayControllerTest : public AshTestBase {
public:
QsRevampUnifiedSystemTrayControllerTest()
: scoped_feature_list_(features::kQsRevamp) {}
QsRevampUnifiedSystemTrayControllerTest(
const QsRevampUnifiedSystemTrayControllerTest&) = delete;
QsRevampUnifiedSystemTrayControllerTest& operator=(
const QsRevampUnifiedSystemTrayControllerTest&) = delete;
~QsRevampUnifiedSystemTrayControllerTest() override = default;
UnifiedSystemTrayControllerTest() = default;
UnifiedSystemTrayControllerTest(const UnifiedSystemTrayControllerTest&) =
delete;
UnifiedSystemTrayControllerTest& operator=(
const UnifiedSystemTrayControllerTest&) = delete;
~UnifiedSystemTrayControllerTest() override = default;
// AshTestBase:
void SetUp() override {
@ -67,20 +64,17 @@ class QsRevampUnifiedSystemTrayControllerTest : public AshTestBase {
// Owned by `widget_`.
raw_ptr<QuickSettingsView, DanglingUntriaged | ExperimentalAsh>
quick_settings_view_;
base::test::ScopedFeatureList scoped_feature_list_;
};
// Tests that setting the `UnifiedSystemTrayModel::StateOnOpen` pref to
// collapsed is a no-op with the QSRevamp enabled.
TEST_F(QsRevampUnifiedSystemTrayControllerTest, ExpandedPrefIsNoOp) {
// collapsed is a no-op.
TEST_F(UnifiedSystemTrayControllerTest, ExpandedPrefIsNoOp) {
// Set the pref to collapsed, there should be no effect.
model_->set_expanded_on_open(UnifiedSystemTrayModel::StateOnOpen::COLLAPSED);
InitializeQuickSettingsView();
EXPECT_TRUE(model_->IsExpandedOnOpen());
EXPECT_TRUE(controller_->IsExpanded());
}
} // namespace ash

@ -26,7 +26,6 @@
#include "ash/system/unified/ime_mode_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_view.h"
#include "ash/system/video_conference/fake_video_conference_tray_controller.h"
#include "ash/system/video_conference/video_conference_tray.h"
#include "ash/test/ash_test_base.h"
@ -59,9 +58,8 @@ constexpr char kQuickSettingsPageCountOnClose[] =
using message_center::MessageCenter;
using message_center::Notification;
class UnifiedSystemTrayTest
: public AshTestBase,
public testing::WithParamInterface<std::tuple<bool, bool>> {
class UnifiedSystemTrayTest : public AshTestBase,
public testing::WithParamInterface<bool> {
public:
UnifiedSystemTrayTest()
: AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
@ -71,20 +69,14 @@ class UnifiedSystemTrayTest
void SetUp() override {
std::vector<base::test::FeatureRef> enabled_features;
std::vector<base::test::FeatureRef> disabled_features;
enabled_features.push_back(features::kCameraEffectsSupportedByHardware);
if (IsQsRevampEnabled()) {
enabled_features.push_back(features::kQsRevamp);
} else {
disabled_features.push_back(features::kQsRevamp);
}
if (IsVcControlsUiEnabled()) {
fake_video_conference_tray_controller_ =
std::make_unique<FakeVideoConferenceTrayController>();
enabled_features.push_back(features::kVideoConference);
}
feature_list_.InitWithFeatures(enabled_features, disabled_features);
feature_list_.InitWithFeatures(enabled_features, {});
AshTestBase::SetUp();
}
@ -96,10 +88,7 @@ class UnifiedSystemTrayTest
}
}
// TODO(b/305075031) clean up after the flag is removed.
bool IsQsRevampEnabled() { return true; }
bool IsVcControlsUiEnabled() { return std::get<1>(GetParam()); }
bool IsVcControlsUiEnabled() { return GetParam(); }
protected:
const std::string AddNotification() {
@ -120,10 +109,8 @@ class UnifiedSystemTrayTest
}
// Show the notification center bubble. This assumes that there is at least
// one notification in the notification list. This should only be called
// when QsRevamp is enabled.
// one notification in the notification list.
void ShowNotificationBubble() {
DCHECK(IsQsRevampEnabled());
Shell::Get()
->GetPrimaryRootWindowController()
->shelf()
@ -133,9 +120,8 @@ class UnifiedSystemTrayTest
}
// Hide the notification center bubble. This assumes that it is already
// shown. This should only be called when QsRevamp is enabled.
// shown.
void HideNotificationBubble() {
DCHECK(IsQsRevampEnabled());
Shell::Get()
->GetPrimaryRootWindowController()
->shelf()
@ -187,17 +173,13 @@ class UnifiedSystemTrayTest
// `DateTray` becomes inactive.
EXPECT_TRUE(tray->is_active());
EXPECT_FALSE(date_tray()->is_active());
// For QsRevamp: the main bubble is shorter than the detailed view bubble.
// The main bubble is shorter than the detailed view bubble.
EXPECT_GT(kQsDetailedViewHeight, bubble_view->height());
}
void CheckDetailedViewHeight(TrayBubbleView* bubble_view) {
if (IsQsRevampEnabled()) {
// The bubble height should be fixed to the detailed view height.
EXPECT_EQ(kQsDetailedViewHeight, bubble_view->height());
} else {
EXPECT_GT(kQsDetailedViewHeight, bubble_view->height());
}
// The bubble height should be fixed to the detailed view height.
EXPECT_EQ(kQsDetailedViewHeight, bubble_view->height());
}
TimeTrayItemView* time_view() {
@ -227,23 +209,18 @@ class UnifiedSystemTrayTest
base::test::ScopedFeatureList feature_list_;
};
INSTANTIATE_TEST_SUITE_P(
All,
UnifiedSystemTrayTest,
testing::Combine(testing::Bool() /* IsQsRevampEnabled() */,
testing::Bool() /* IsVcControlsUiEnabled() */));
INSTANTIATE_TEST_SUITE_P(All,
UnifiedSystemTrayTest,
testing::Bool() /*IsVcControlsUiEnabled()*/);
// Regression test for crbug/1360579
TEST_P(UnifiedSystemTrayTest, GetAccessibleNameForQuickSettingsBubble) {
auto* tray = GetPrimaryUnifiedSystemTray();
tray->ShowBubble();
EXPECT_EQ(
tray->GetAccessibleNameForQuickSettingsBubble(),
l10n_util::GetStringUTF16(
IsQsRevampEnabled()
? IDS_ASH_REVAMPED_QUICK_SETTINGS_BUBBLE_ACCESSIBLE_DESCRIPTION
: IDS_ASH_QUICK_SETTINGS_BUBBLE_ACCESSIBLE_DESCRIPTION));
EXPECT_EQ(tray->GetAccessibleNameForQuickSettingsBubble(),
l10n_util::GetStringUTF16(
IDS_ASH_QUICK_SETTINGS_BUBBLE_ACCESSIBLE_DESCRIPTION));
}
TEST_P(UnifiedSystemTrayTest, ShowVolumeSliderBubble) {
@ -379,32 +356,15 @@ TEST_P(UnifiedSystemTrayTest, FocusQuickSettings) {
auto* tray = GetPrimaryUnifiedSystemTray();
tray->ShowBubble();
if (IsQsRevampEnabled()) {
auto* quick_settings_view = tray->bubble()->quick_settings_view();
auto* focus_manager = quick_settings_view->GetFocusManager();
EXPECT_FALSE(
quick_settings_view->Contains(focus_manager->GetFocusedView()));
auto* quick_settings_view = tray->bubble()->quick_settings_view();
auto* focus_manager = quick_settings_view->GetFocusManager();
EXPECT_FALSE(quick_settings_view->Contains(focus_manager->GetFocusedView()));
// There's no `FocusQuickSettings` method in the new view. Press the tab key
// should focus on the first button in the qs bubble.
ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
generator.PressKey(ui::KeyboardCode::VKEY_TAB, ui::EF_NONE);
EXPECT_TRUE(quick_settings_view->Contains(focus_manager->GetFocusedView()));
return;
}
auto* unified_system_tray_view = tray->bubble()->unified_view();
auto* focus_manager = unified_system_tray_view->GetFocusManager();
EXPECT_FALSE(
unified_system_tray_view->Contains(focus_manager->GetFocusedView()));
auto did_focus = tray->FocusQuickSettings(false);
EXPECT_TRUE(did_focus);
EXPECT_TRUE(
unified_system_tray_view->Contains(focus_manager->GetFocusedView()));
// There's no `FocusQuickSettings` method in the new view. Press the tab key
// should focus on the first button in the qs bubble.
ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
generator.PressKey(ui::KeyboardCode::VKEY_TAB, ui::EF_NONE);
EXPECT_TRUE(quick_settings_view->Contains(focus_manager->GetFocusedView()));
}
TEST_P(UnifiedSystemTrayTest, FocusQuickSettings_BubbleNotShown) {
@ -428,21 +388,10 @@ TEST_P(UnifiedSystemTrayTest, FocusQuickSettings_VoxEnabled) {
EXPECT_TRUE(did_focus);
if (IsQsRevampEnabled()) {
auto* quick_settings_view = tray->bubble()->quick_settings_view();
auto* focus_manager = quick_settings_view->GetFocusManager();
EXPECT_TRUE(tray_bubble_widget->IsActive());
EXPECT_FALSE(
quick_settings_view->Contains(focus_manager->GetFocusedView()));
return;
}
auto* unified_system_tray_view = tray->bubble()->unified_view();
auto* focus_manager = unified_system_tray_view->GetFocusManager();
auto* quick_settings_view = tray->bubble()->quick_settings_view();
auto* focus_manager = quick_settings_view->GetFocusManager();
EXPECT_TRUE(tray_bubble_widget->IsActive());
EXPECT_FALSE(
unified_system_tray_view->Contains(focus_manager->GetFocusedView()));
EXPECT_FALSE(quick_settings_view->Contains(focus_manager->GetFocusedView()));
}
TEST_P(UnifiedSystemTrayTest, TimeInQuickSettingsMetric) {
@ -480,7 +429,7 @@ TEST_P(UnifiedSystemTrayTest, TimeInQuickSettingsMetric) {
}
// Tests that the number of quick settings pages is recorded when the QS bubble
// is closed. Tests that the metric is not recorded when QsRevamp is disabled.
// is closed.
TEST_P(UnifiedSystemTrayTest, QuickSettingsPageCountMetric) {
base::HistogramTester histogram_tester;
@ -496,12 +445,10 @@ TEST_P(UnifiedSystemTrayTest, QuickSettingsPageCountMetric) {
// Close the bubble and verify that the metric is recorded.
tray->CloseBubble();
histogram_tester.ExpectTotalCount(kQuickSettingsPageCountOnClose,
IsQsRevampEnabled() ? 1 : 0);
histogram_tester.ExpectBucketCount(
kQuickSettingsPageCountOnClose,
/*sample=*/1,
/*expected_count=*/IsQsRevampEnabled() ? 1 : 0);
histogram_tester.ExpectTotalCount(kQuickSettingsPageCountOnClose, 1);
histogram_tester.ExpectBucketCount(kQuickSettingsPageCountOnClose,
/*sample=*/1,
/*expected_count=*/1);
// Show the bubble with two pages, and verify that the metric is recorded when
// the bubble is closed.
@ -512,16 +459,13 @@ TEST_P(UnifiedSystemTrayTest, QuickSettingsPageCountMetric) {
->pagination_model()
->SetTotalPages(2);
tray->CloseBubble();
histogram_tester.ExpectTotalCount(kQuickSettingsPageCountOnClose,
IsQsRevampEnabled() ? 2 : 0);
histogram_tester.ExpectBucketCount(
kQuickSettingsPageCountOnClose,
/*sample=*/2,
/*expected_count=*/IsQsRevampEnabled() ? 1 : 0);
histogram_tester.ExpectBucketCount(
kQuickSettingsPageCountOnClose,
/*sample=*/1,
/*expected_count=*/IsQsRevampEnabled() ? 1 : 0);
histogram_tester.ExpectTotalCount(kQuickSettingsPageCountOnClose, 2);
histogram_tester.ExpectBucketCount(kQuickSettingsPageCountOnClose,
/*sample=*/2,
/*expected_count=*/1);
histogram_tester.ExpectBucketCount(kQuickSettingsPageCountOnClose,
/*sample=*/1,
/*expected_count=*/1);
}
// Tests that pressing the TOGGLE_CALENDAR accelerator once results in the
@ -559,8 +503,7 @@ TEST_P(UnifiedSystemTrayTest, CalendarAcceleratorFocusesDateCell) {
}
// Tests that using functional keys to change brightness/volume when the
// `CalendarView` is open will make ink drop transfer(before and after
// QsRevamp) and bubble height change(after QsRevamp).
// `CalendarView` is open will make ink drop transfer and bubble height change.
TEST_P(UnifiedSystemTrayTest, CalendarGoesToMainViewByFunctionalKeys) {
auto* tray = GetPrimaryUnifiedSystemTray();
tray->ShowBubble();
@ -574,7 +517,7 @@ TEST_P(UnifiedSystemTrayTest, CalendarGoesToMainViewByFunctionalKeys) {
// Tests the volume up/down/mute functional keys. It should hide the calendar
// view and open the `unified_system_tray_bubble_`. The ink drop should
// transfer from `DateTray` to `UnifiedSystemTray` and the `bubble_view`
// should shrink for the revamped Qs main page.
// should shrink for the Qs main page.
TransferFromCalendarViewToMainViewByFuncKeys(tray, bubble_view,
ui::VKEY_VOLUME_UP);
TransferFromCalendarViewToMainViewByFuncKeys(tray, bubble_view,
@ -783,11 +726,6 @@ TEST_P(UnifiedSystemTrayTest, TrayBackgroundColorAfterSwitchToTabletMode) {
// Tests that the bubble automatically hides if it is visible when another
// bubble becomes visible, and otherwise does not automatically show or hide.
TEST_P(UnifiedSystemTrayTest, BubbleHideBehavior) {
// This hiding behavior only applies when QsRevamp is enabled.
if (!IsQsRevampEnabled()) {
return;
}
// Basic verification test that the unified system tray bubble can show/hide
// itself when no other bubbles are visible.
auto* tray = GetPrimaryUnifiedSystemTray();
@ -860,11 +798,6 @@ TEST_P(UnifiedSystemTrayTest, BubbleViewSizeChangeNoEnoughSpace) {
}
TEST_P(UnifiedSystemTrayTest, BubbleViewSizeChangeWithBigMainPage) {
// No QuickSettingsView in the old unified system bubble.
if (!IsQsRevampEnabled()) {
return;
}
// Set a large enough screen size.
UpdateDisplay("1600x900");

@ -1,508 +0,0 @@
// 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_view.h"
#include <numeric>
#include "ash/public/cpp/shelf_config.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/system/media/unified_media_controls_container.h"
#include "ash/system/message_center/ash_message_center_lock_screen_controller.h"
#include "ash/system/notification_center/notification_center_view.h"
#include "ash/system/tray/interacted_by_tap_recorder.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/unified/detailed_view_controller.h"
#include "ash/system/unified/feature_pod_button.h"
#include "ash/system/unified/feature_pods_container_view.h"
#include "ash/system/unified/notification_hidden_view.h"
#include "ash/system/unified/page_indicator_view.h"
#include "ash/system/unified/top_shortcuts_view.h"
#include "ash/system/unified/unified_system_info_view.h"
#include "ash/system/unified/unified_system_tray_controller.h"
#include "ash/system/unified/unified_system_tray_model.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "base/memory/raw_ptr.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/scoped_canvas.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/public/cpp/message_center_constants.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/focus/focus_search.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/painter.h"
namespace ash {
namespace {
// The padding between sliders inside the `UnifiedSlidersContainerView`.
const int kSlidersPadding = 8;
class DetailedViewContainer : public views::View {
public:
DetailedViewContainer() = default;
DetailedViewContainer(const DetailedViewContainer&) = delete;
DetailedViewContainer& operator=(const DetailedViewContainer&) = delete;
~DetailedViewContainer() override = default;
// views::View:
void Layout() override {
for (auto* child : children()) {
child->SetBoundsRect(GetContentsBounds());
}
views::View::Layout();
}
const char* GetClassName() const override { return "DetailedViewContainer"; }
};
class AccessibilityFocusHelperView : public views::View {
public:
AccessibilityFocusHelperView(UnifiedSystemTrayController* controller)
: controller_(controller) {}
bool HandleAccessibleAction(const ui::AXActionData& action_data) override {
GetFocusManager()->ClearFocus();
GetFocusManager()->SetStoredFocusView(nullptr);
controller_->FocusOut(false);
return true;
}
// views::View:
void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
node_data->role = ax::mojom::Role::kListItem;
}
private:
raw_ptr<UnifiedSystemTrayController, DanglingUntriaged | ExperimentalAsh>
controller_;
};
} // namespace
UnifiedSlidersContainerView::UnifiedSlidersContainerView(
bool initially_expanded)
: expanded_amount_(initially_expanded ? 1.0 : 0.0) {
SetVisible(initially_expanded);
}
UnifiedSlidersContainerView::~UnifiedSlidersContainerView() = default;
void UnifiedSlidersContainerView::SetExpandedAmount(double expanded_amount) {
DCHECK(0.0 <= expanded_amount && expanded_amount <= 1.0);
SetVisible(expanded_amount > 0.0);
expanded_amount_ = expanded_amount;
InvalidateLayout();
UpdateOpacity();
}
int UnifiedSlidersContainerView::GetExpandedHeight() const {
return std::accumulate(
children().cbegin(), children().cend(), 0, [](int height, const auto* v) {
return height + v->GetHeightForWidth(kTrayMenuWidth) + kSlidersPadding;
});
}
void UnifiedSlidersContainerView::Layout() {
int y = 0;
for (auto* child : children()) {
int height = child->GetHeightForWidth(kTrayMenuWidth);
child->SetBounds(0, y, kTrayMenuWidth, height);
y += height + kSlidersPadding;
}
}
gfx::Size UnifiedSlidersContainerView::CalculatePreferredSize() const {
return gfx::Size(kTrayMenuWidth, GetExpandedHeight() * expanded_amount_);
}
const char* UnifiedSlidersContainerView::GetClassName() const {
return "UnifiedSlidersContainerView";
}
void UnifiedSlidersContainerView::UpdateOpacity() {
const int height = GetPreferredSize().height();
for (auto* child : children()) {
double opacity = 1.0;
if (child->y() > height) {
opacity = 0.0;
} else if (child->bounds().bottom() < height) {
opacity = 1.0;
} else {
const double ratio =
static_cast<double>(height - child->y()) / child->height();
// TODO(tetsui): Confirm the animation curve with UX.
opacity = std::max(0., 2. * ratio - 1.);
}
child->layer()->SetOpacity(opacity);
}
}
// The container view for the system tray, i.e. the panel containing settings
// buttons and sliders (e.g. sign out, lock, volume slider, etc.).
class UnifiedSystemTrayView::SystemTrayContainer : public views::View {
public:
SystemTrayContainer()
: layout_manager_(SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical))) {}
SystemTrayContainer(const SystemTrayContainer&) = delete;
SystemTrayContainer& operator=(const SystemTrayContainer&) = delete;
~SystemTrayContainer() override = default;
void SetFlexForView(views::View* view) {
DCHECK_EQ(view->parent(), this);
layout_manager_->SetFlexForView(view, 1);
}
// views::View:
void ChildPreferredSizeChanged(views::View* child) override {
PreferredSizeChanged();
}
const char* GetClassName() const override { return "SystemTrayContainer"; }
private:
const raw_ptr<views::BoxLayout, ExperimentalAsh> layout_manager_;
};
UnifiedSystemTrayView::UnifiedSystemTrayView(
UnifiedSystemTrayController* controller,
bool initially_expanded)
: expanded_amount_(initially_expanded ? 1.0 : 0.0),
controller_(controller),
notification_hidden_view_(new NotificationHiddenView()),
top_shortcuts_view_(new TopShortcutsView(controller_)),
feature_pods_container_(
new FeaturePodsContainerView(controller_, initially_expanded)),
page_indicator_view_(
new PageIndicatorView(controller_, initially_expanded)),
sliders_container_(new UnifiedSlidersContainerView(initially_expanded)),
system_info_view_(new UnifiedSystemInfoView(controller_)),
system_tray_container_(new SystemTrayContainer()),
detailed_view_container_(new DetailedViewContainer()),
media_controls_container_(new UnifiedMediaControlsContainer()),
focus_search_(std::make_unique<views::FocusSearch>(this, false, false)),
interacted_by_tap_recorder_(
std::make_unique<InteractedByTapRecorder>(this)) {
DCHECK(controller_);
auto add_layered_child = [](views::View* parent, views::View* child) {
parent->AddChildView(child);
};
SessionControllerImpl* session_controller =
Shell::Get()->session_controller();
notification_hidden_view_->SetVisible(
session_controller->GetUserSession(0) &&
session_controller->IsScreenLocked() &&
!AshMessageCenterLockScreenController::IsEnabled());
add_layered_child(system_tray_container_, notification_hidden_view_);
AddChildView(system_tray_container_.get());
add_layered_child(system_tray_container_, top_shortcuts_view_);
system_tray_container_->AddChildView(feature_pods_container_.get());
system_tray_container_->AddChildView(page_indicator_view_.get());
system_tray_container_->AddChildView(media_controls_container_.get());
media_controls_container_->SetExpandedAmount(expanded_amount_);
system_tray_container_->AddChildView(sliders_container_.get());
add_layered_child(system_tray_container_, system_info_view_);
system_tray_container_->SetFlexForView(page_indicator_view_);
detailed_view_container_->SetVisible(false);
add_layered_child(this, detailed_view_container_);
top_shortcuts_view_->SetExpandedAmount(expanded_amount_);
system_tray_container_->AddChildView(
new AccessibilityFocusHelperView(controller_));
}
UnifiedSystemTrayView::~UnifiedSystemTrayView() = default;
void UnifiedSystemTrayView::SetMaxHeight(int max_height) {
max_height_ = max_height;
// FeaturePodsContainer can adjust it's height by reducing the number of rows
// it uses. It will calculate how many rows to use based on the max height
// passed here.
feature_pods_container_->SetMaxHeight(
max_height - top_shortcuts_view_->GetPreferredSize().height() -
page_indicator_view_->GetPreferredSize().height() -
media_controls_container_->GetExpandedHeight() -
sliders_container_->GetExpandedHeight() -
system_info_view_->GetPreferredSize().height());
}
void UnifiedSystemTrayView::AddFeaturePodButton(FeaturePodButton* button) {
feature_pods_container_->AddFeaturePodButton(button);
}
void UnifiedSystemTrayView::AddSliderView(
std::unique_ptr<views::View> slider_view) {
sliders_container_->AddChildView(std::move(slider_view));
}
void UnifiedSystemTrayView::AddMediaControlsView(views::View* media_controls) {
DCHECK(media_controls);
media_controls->SetPaintToLayer();
media_controls->layer()->SetFillsBoundsOpaquely(false);
media_controls_container_->AddChildView(media_controls);
}
void UnifiedSystemTrayView::ShowMediaControls() {
media_controls_container_->SetShouldShowMediaControls(true);
if (detailed_view_container_->GetVisible()) {
return;
}
if (media_controls_container_->MaybeShowMediaControls()) {
PreferredSizeChanged();
}
}
void UnifiedSystemTrayView::SetDetailedView(
std::unique_ptr<views::View> detailed_view) {
auto system_tray_size = system_tray_container_->GetPreferredSize();
system_tray_container_->SetVisible(false);
detailed_view_container_->RemoveAllChildViews();
views::View* view =
detailed_view_container_->AddChildView(std::move(detailed_view));
detailed_view_container_->SetVisible(true);
detailed_view_container_->SetPreferredSize(system_tray_size);
view->InvalidateLayout();
Layout();
}
void UnifiedSystemTrayView::ResetDetailedView() {
detailed_view_container_->RemoveAllChildViews();
detailed_view_container_->SetVisible(false);
media_controls_container_->MaybeShowMediaControls();
system_tray_container_->SetVisible(true);
sliders_container_->UpdateOpacity();
PreferredSizeChanged();
Layout();
}
void UnifiedSystemTrayView::SaveFocus() {
auto* focus_manager = GetFocusManager();
if (!focus_manager) {
return;
}
saved_focused_view_ = focus_manager->GetFocusedView();
}
void UnifiedSystemTrayView::RestoreFocus() {
if (saved_focused_view_) {
saved_focused_view_->RequestFocus();
}
}
void UnifiedSystemTrayView::SetExpandedAmount(double expanded_amount) {
DCHECK(0.0 <= expanded_amount && expanded_amount <= 1.0);
expanded_amount_ = expanded_amount;
top_shortcuts_view_->SetExpandedAmount(expanded_amount);
feature_pods_container_->SetExpandedAmount(expanded_amount);
page_indicator_view_->SetExpandedAmount(expanded_amount);
media_controls_container_->SetExpandedAmount(expanded_amount);
sliders_container_->SetExpandedAmount(expanded_amount);
PreferredSizeChanged();
// It is possible that the ratio between |message_center_view_| and others
// can change while the bubble size remain unchanged.
Layout();
}
int UnifiedSystemTrayView::GetExpandedSystemTrayHeight() const {
return (notification_hidden_view_->GetVisible()
? notification_hidden_view_->GetPreferredSize().height()
: 0) +
top_shortcuts_view_->GetPreferredSize().height() +
feature_pods_container_->GetExpandedHeight() +
page_indicator_view_->GetExpandedHeight() +
sliders_container_->GetExpandedHeight() +
media_controls_container_->GetExpandedHeight() +
system_info_view_->GetPreferredSize().height();
}
int UnifiedSystemTrayView::GetCollapsedSystemTrayHeight() const {
return (notification_hidden_view_->GetVisible()
? notification_hidden_view_->GetPreferredSize().height()
: 0) +
top_shortcuts_view_->GetPreferredSize().height() +
feature_pods_container_->GetCollapsedHeight() +
system_info_view_->GetPreferredSize().height();
}
int UnifiedSystemTrayView::GetCurrentHeight() const {
return GetPreferredSize().height();
}
int UnifiedSystemTrayView::GetVisibleFeaturePodCount() const {
return feature_pods_container_->GetVisibleCount();
}
std::u16string UnifiedSystemTrayView::GetDetailedViewAccessibleName() const {
return controller_->detailed_view_controller()->GetAccessibleName();
}
bool UnifiedSystemTrayView::IsDetailedViewShown() const {
return detailed_view_container_->GetVisible();
}
views::View* UnifiedSystemTrayView::GetFirstFocusableChild() {
FocusTraversable* focus_traversable = GetFocusTraversable();
views::View* focus_traversable_view = this;
return focus_search_->FindNextFocusableView(
nullptr, views::FocusSearch::SearchDirection::kForwards,
views::FocusSearch::TraversalDirection::kDown,
views::FocusSearch::StartingViewPolicy::kSkipStartingView,
views::FocusSearch::AnchoredDialogPolicy::kCanGoIntoAnchoredDialog,
&focus_traversable, &focus_traversable_view);
}
views::View* UnifiedSystemTrayView::GetLastFocusableChild() {
FocusTraversable* focus_traversable = GetFocusTraversable();
views::View* focus_traversable_view = this;
return focus_search_->FindNextFocusableView(
nullptr, views::FocusSearch::SearchDirection::kBackwards,
views::FocusSearch::TraversalDirection::kDown,
views::FocusSearch::StartingViewPolicy::kSkipStartingView,
views::FocusSearch::AnchoredDialogPolicy::kCanGoIntoAnchoredDialog,
&focus_traversable, &focus_traversable_view);
}
void UnifiedSystemTrayView::FocusEntered(bool reverse) {
views::View* focus_view =
reverse ? GetLastFocusableChild() : GetFirstFocusableChild();
GetFocusManager()->ClearFocus();
GetFocusManager()->SetFocusedView(focus_view);
}
gfx::Size UnifiedSystemTrayView::CalculatePreferredSize() const {
int expanded_height = GetExpandedSystemTrayHeight();
int collapsed_height = GetCollapsedSystemTrayHeight();
return gfx::Size(kTrayMenuWidth,
collapsed_height + ((expanded_height - collapsed_height) *
expanded_amount_));
}
void UnifiedSystemTrayView::OnGestureEvent(ui::GestureEvent* event) {
gfx::PointF screen_location = event->root_location_f();
switch (event->type()) {
case ui::ET_GESTURE_SCROLL_BEGIN:
controller_->BeginDrag(screen_location);
event->SetHandled();
break;
case ui::ET_GESTURE_SCROLL_UPDATE:
controller_->UpdateDrag(screen_location);
event->SetHandled();
break;
case ui::ET_GESTURE_END:
controller_->EndDrag(screen_location);
event->SetHandled();
break;
case ui::ET_SCROLL_FLING_START:
controller_->Fling(event->details().velocity_y());
break;
default:
break;
}
}
void UnifiedSystemTrayView::Layout() {
if (system_tray_container_->GetVisible()) {
system_tray_container_->SetBoundsRect(GetContentsBounds());
} else if (detailed_view_container_->GetVisible()) {
detailed_view_container_->SetBoundsRect(GetContentsBounds());
}
}
void UnifiedSystemTrayView::ChildPreferredSizeChanged(views::View* child) {
// The size change is not caused by SetExpandedAmount(), because they don't
// trigger PreferredSizeChanged().
PreferredSizeChanged();
}
const char* UnifiedSystemTrayView::GetClassName() const {
return "UnifiedSystemTrayView";
}
void UnifiedSystemTrayView::AddedToWidget() {
focus_manager_ = GetFocusManager();
if (focus_manager_) {
focus_manager_->AddFocusChangeListener(this);
}
}
void UnifiedSystemTrayView::RemovedFromWidget() {
if (!focus_manager_) {
return;
}
focus_manager_->RemoveFocusChangeListener(this);
focus_manager_ = nullptr;
}
views::FocusTraversable* UnifiedSystemTrayView::GetFocusTraversable() {
return this;
}
views::FocusSearch* UnifiedSystemTrayView::GetFocusSearch() {
return focus_search_.get();
}
views::FocusTraversable* UnifiedSystemTrayView::GetFocusTraversableParent() {
return nullptr;
}
views::View* UnifiedSystemTrayView::GetFocusTraversableParentView() {
return this;
}
void UnifiedSystemTrayView::OnWillChangeFocus(views::View* before,
views::View* now) {}
void UnifiedSystemTrayView::OnDidChangeFocus(views::View* before,
views::View* now) {
if (feature_pods_container_->Contains(now)) {
feature_pods_container_->EnsurePageWithButton(now);
}
views::View* first_view = GetFirstFocusableChild();
views::View* last_view = GetLastFocusableChild();
bool focused_out = false;
if (before == last_view && now == first_view) {
focused_out = controller_->FocusOut(false);
} else if (before == first_view && now == last_view) {
focused_out = controller_->FocusOut(true);
}
if (focused_out) {
GetFocusManager()->ClearFocus();
GetFocusManager()->SetStoredFocusView(nullptr);
}
}
} // namespace ash

@ -1,220 +0,0 @@
// 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.
#ifndef ASH_SYSTEM_UNIFIED_UNIFIED_SYSTEM_TRAY_VIEW_H_
#define ASH_SYSTEM_UNIFIED_UNIFIED_SYSTEM_TRAY_VIEW_H_
#include <memory>
#include "ash/ash_export.h"
#include "base/memory/raw_ptr.h"
#include "ui/views/focus/focus_manager.h"
#include "ui/views/view.h"
namespace ash {
class FeaturePodButton;
class FeaturePodsContainerView;
class TopShortcutsView;
class UnifiedMediaControlsContainer;
class NotificationHiddenView;
class PageIndicatorView;
class UnifiedSystemInfoView;
class UnifiedSystemTrayController;
// Container view of slider views. If SetExpandedAmount() is called with 1.0,
// the behavior is same as vertiacal BoxLayout, but otherwise it shows
// intermediate state during animation.
class UnifiedSlidersContainerView : public views::View {
public:
explicit UnifiedSlidersContainerView(bool initially_expanded);
UnifiedSlidersContainerView(const UnifiedSlidersContainerView&) = delete;
UnifiedSlidersContainerView& operator=(const UnifiedSlidersContainerView&) =
delete;
~UnifiedSlidersContainerView() override;
// Change the expanded state. 0.0 if collapsed, and 1.0 if expanded.
// Otherwise, it shows intermediate state.
void SetExpandedAmount(double expanded_amount);
// Get height of the view when |expanded_amount| is set to 1.0.
int GetExpandedHeight() const;
// Update opacity of each child slider views based on |expanded_amount_|.
void UpdateOpacity();
// views::View:
void Layout() override;
gfx::Size CalculatePreferredSize() const override;
const char* GetClassName() const override;
private:
double expanded_amount_;
};
// View class of the main bubble in UnifiedSystemTray.
//
// The UnifiedSystemTray contains two sub components:
// 1. MessageCenter: contains the list of notifications
// 2. SystemTray: contains quick settings controls
// Note that the term "UnifiedSystemTray" refers to entire bubble containing
// both (1) and (2).
class ASH_EXPORT UnifiedSystemTrayView : public views::View,
public views::FocusTraversable,
public views::FocusChangeListener {
public:
UnifiedSystemTrayView(UnifiedSystemTrayController* controller,
bool initially_expanded);
UnifiedSystemTrayView(const UnifiedSystemTrayView&) = delete;
UnifiedSystemTrayView& operator=(const UnifiedSystemTrayView&) = delete;
~UnifiedSystemTrayView() override;
// Set the maximum height that the view can take.
void SetMaxHeight(int max_height);
// Add feature pod button to |feature_pods_|.
void AddFeaturePodButton(FeaturePodButton* button);
// Add slider view.
void AddSliderView(std::unique_ptr<views::View> slider_view);
// Add media controls view to |media_controls_container_|;
void AddMediaControlsView(views::View* media_controls);
// Hide the main view and show the given |detailed_view|.
void SetDetailedView(std::unique_ptr<views::View> detailed_view);
// Remove the detailed view set by SetDetailedView, and show the main view.
// It deletes |detailed_view| and children.
void ResetDetailedView();
// Save and restore keyboard focus of the currently focused element. Called
// before transitioning into a detailed view.
void SaveFocus();
void RestoreFocus();
// Set the first child view to be focused when focus is acquired.
// This is the first visible child unless reverse is true, in which case
// it is the last visible child.
void FocusEntered(bool reverse);
// Change the expanded state. 0.0 if collapsed, and 1.0 if expanded.
// Otherwise, it shows intermediate state. This is triggered during the
// progress of expand/collapse animation, updating the children accordingly.
void SetExpandedAmount(double expanded_amount);
// Get height of the system tray (excluding the message center) when
// |expanded_amount| is set to 1.0.
//
// Note that this function is used to calculate the transform-based
// collapse/expand animation, which is currently only enabled when there are
// no notifications.
int GetExpandedSystemTrayHeight() const;
// Get height of the system menu (excluding the message center) when
// |expanded_amount| is set to 0.0.
int GetCollapsedSystemTrayHeight() const;
// Get current height of the view (including the message center).
int GetCurrentHeight() const;
// Returns the number of visible feature pods.
int GetVisibleFeaturePodCount() const;
// Get the accessible name for the currently shown detailed view.
std::u16string GetDetailedViewAccessibleName() const;
// Returns true if a detailed view is being shown in the tray. (e.g Bluetooth
// Settings).
bool IsDetailedViewShown() const;
// Show media controls view.
void ShowMediaControls();
// views::View:
gfx::Size CalculatePreferredSize() const override;
void OnGestureEvent(ui::GestureEvent* event) override;
void Layout() override;
void ChildPreferredSizeChanged(views::View* child) override;
const char* GetClassName() const override;
views::FocusTraversable* GetFocusTraversable() override;
void AddedToWidget() override;
void RemovedFromWidget() override;
// views::FocusTraversable:
views::FocusSearch* GetFocusSearch() override;
views::FocusTraversable* GetFocusTraversableParent() override;
views::View* GetFocusTraversableParentView() override;
// views::FocusChangeListener:
void OnWillChangeFocus(views::View* before, views::View* now) override;
void OnDidChangeFocus(views::View* before, views::View* now) override;
FeaturePodsContainerView* feature_pods_container() {
return feature_pods_container_;
}
View* detailed_view_container() { return detailed_view_container_; }
NotificationHiddenView* notification_hidden_view_for_testing() {
return notification_hidden_view_;
}
PageIndicatorView* page_indicator_view_for_test() {
return page_indicator_view_;
}
UnifiedMediaControlsContainer* media_controls_container_for_testing() {
return media_controls_container_;
}
private:
class SystemTrayContainer;
friend class UnifiedMessageCenterBubbleTest;
// Get first and last focusable child views. These functions are used to
// figure out if we need to focus out or to set the correct focused view
// when focus is acquired from another widget.
View* GetFirstFocusableChild();
View* GetLastFocusableChild();
double expanded_amount_;
// Unowned.
const raw_ptr<UnifiedSystemTrayController,
DanglingUntriaged | ExperimentalAsh>
controller_;
// Owned by views hierarchy.
const raw_ptr<NotificationHiddenView, ExperimentalAsh>
notification_hidden_view_;
const raw_ptr<TopShortcutsView, ExperimentalAsh> top_shortcuts_view_;
const raw_ptr<FeaturePodsContainerView, ExperimentalAsh>
feature_pods_container_;
const raw_ptr<PageIndicatorView, ExperimentalAsh> page_indicator_view_;
const raw_ptr<UnifiedSlidersContainerView, ExperimentalAsh>
sliders_container_;
const raw_ptr<UnifiedSystemInfoView, ExperimentalAsh> system_info_view_;
const raw_ptr<SystemTrayContainer, ExperimentalAsh> system_tray_container_;
const raw_ptr<views::View, ExperimentalAsh> detailed_view_container_;
const raw_ptr<UnifiedMediaControlsContainer, ExperimentalAsh>
media_controls_container_;
// The maximum height available to the view.
int max_height_ = 0;
// The view that is saved by calling SaveFocus().
raw_ptr<views::View, ExperimentalAsh> saved_focused_view_ = nullptr;
const std::unique_ptr<views::FocusSearch> focus_search_;
raw_ptr<views::FocusManager, ExperimentalAsh> focus_manager_ = nullptr;
const std::unique_ptr<ui::EventHandler> interacted_by_tap_recorder_;
};
} // namespace ash
#endif // ASH_SYSTEM_UNIFIED_UNIFIED_SYSTEM_TRAY_VIEW_H_

@ -22,7 +22,6 @@
#include "ash/system/tray/tray_constants.h"
#include "ash/system/tray/tray_popup_utils.h"
#include "ash/system/tray/tri_view.h"
#include "ash/system/unified/top_shortcuts_view.h"
#include "ash/system/unified/user_chooser_detailed_view_controller.h"
#include "base/functional/bind.h"
#include "base/strings/utf_string_conversions.h"

@ -17,7 +17,6 @@
#include "ash/system/tray/hover_highlight_view.h"
#include "ash/system/unified/unified_system_tray.h"
#include "ash/system/unified/unified_system_tray_bubble.h"
#include "ash/system/unified/unified_system_tray_view.h"
#include "base/memory/raw_ptr.h"
#include "chrome/browser/ash/login/login_manager_test.h"
#include "chrome/browser/ash/login/session/user_session_manager.h"