0

Reland "Added the display subpage for QsRevamp"

This is a reland of commit 5ccff3bed9

The root cause of the revert issue:
In this CL(
https://chromium-review.googlesource.com/c/chromium/src/+/4195759), we
identified the issue with the brightness slider that it cannot be
changed via keyboard, and this issue caused the tast test failure.

The fix:
We fixed the issue by reverting the logic for brightness slider(combined
the change in the above linked CL with the original CL for the display
subpage).

Original change's description:
> Added the display subpage for QsRevamp
>
> 1. Added the display detailed view for QsRevamp. This view contains a
>    night light feature tile, a dark mode feature tile, and a display
>    brightness slider. The title of this view is `Display` and there is a
>    settings button leading to the display system settings page at the
>    end of the title row.
> 2. Added corresponding unit test for display detailed view and
>    parameterize unit tests for night light and dark mode feature pod
>    controllers.
> 3. Applied the callback for the night light button in the main page.
> 4. Added the vector icon resource for the night light when disabled.
>    Added the string for the display detailed view title.
> 5. Fixed an existing issue with the brightness slider by adding a
>    variable in unified_system_tray_model to store the actual brightness
>    value to render the slider.
>
> Display detailed view overview(locked screen & logged-in screen):
> https://screenshot.googleplex.com/4BDGFDMAv3By5UW
>
> Night Light feature tile different sub labels:
> https://screenshot.googleplex.com/5Uw9bbGimcvd9bG
>
> Dark Mode feature tile different sub labels:
> https://screenshot.googleplex.com/4KTbcabeF2n2x53
>
> Focus behavior:
> https://screenshot.googleplex.com/5dJtJUsPyrHbdQz
>
> Bug: b/252870817
> Change-Id: I7b89b2d21096c94b4d96280c01111a3129dd3b51
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4174144
> Reviewed-by: Jiaming Cheng <jiamingc@chromium.org>
> Reviewed-by: Mitsuru Oshima <oshima@chromium.org>
> Commit-Queue: Sylvie Liu <sylvieliu@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1097004}

Bug: b/252870817
Change-Id: I4c4d86eed53df82e3e6ce57a41c6f668394e9b01
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4198495
Reviewed-by: Alex Newcomer <newcomer@chromium.org>
Reviewed-by: Mitsuru Oshima <oshima@chromium.org>
Commit-Queue: Sylvie Liu <sylvieliu@chromium.org>
Reviewed-by: Jiaming Cheng <jiamingc@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1097621}
This commit is contained in:
Sylvie Liu
2023-01-26 21:58:54 +00:00
committed by Chromium LUCI CQ
parent 4f2ddafd32
commit 1fbe4538d6
29 changed files with 1157 additions and 244 deletions

@@ -1190,6 +1190,10 @@ component("ash") {
"system/bluetooth/bluetooth_notification_controller.h", "system/bluetooth/bluetooth_notification_controller.h",
"system/brightness/brightness_controller_chromeos.cc", "system/brightness/brightness_controller_chromeos.cc",
"system/brightness/brightness_controller_chromeos.h", "system/brightness/brightness_controller_chromeos.h",
"system/brightness/display_detailed_view.cc",
"system/brightness/display_detailed_view.h",
"system/brightness/quick_settings_display_detailed_view_controller.cc",
"system/brightness/quick_settings_display_detailed_view_controller.h",
"system/brightness/unified_brightness_slider_controller.cc", "system/brightness/unified_brightness_slider_controller.cc",
"system/brightness/unified_brightness_slider_controller.h", "system/brightness/unified_brightness_slider_controller.h",
"system/brightness/unified_brightness_view.cc", "system/brightness/unified_brightness_view.cc",
@@ -3027,6 +3031,7 @@ test("ash_unittests") {
"system/bluetooth/fake_bluetooth_detailed_view.h", "system/bluetooth/fake_bluetooth_detailed_view.h",
"system/bluetooth/fake_bluetooth_device_list_controller.cc", "system/bluetooth/fake_bluetooth_device_list_controller.cc",
"system/bluetooth/fake_bluetooth_device_list_controller.h", "system/bluetooth/fake_bluetooth_device_list_controller.h",
"system/brightness/display_detailed_view_unittest.cc",
"system/brightness/unified_brightness_view_unittest.cc", "system/brightness/unified_brightness_view_unittest.cc",
"system/camera/autozoom_feature_pod_controller_unittest.cc", "system/camera/autozoom_feature_pod_controller_unittest.cc",
"system/camera/autozoom_toast_controller_unittest.cc", "system/camera/autozoom_toast_controller_unittest.cc",

@@ -2703,6 +2703,9 @@ Connect your device to power.
<message name="IDS_ASH_STATUS_TRAY_AUDIO_INPUT" desc="The label used in audio detailed page for audio input section of ash tray pop up."> <message name="IDS_ASH_STATUS_TRAY_AUDIO_INPUT" desc="The label used in audio detailed page for audio input section of ash tray pop up.">
Input Input
</message> </message>
<message name="IDS_ASH_STATUS_TRAY_DISPLAY" desc="The label used for the button in the status tray to show the display detailed page.">
Display
</message>
<message name="IDS_ASH_STATUS_TRAY_DISPLAY_REMOVED_EXCEEDED_MAXIMUM" desc="The label used when user connects more external displays than the maximum that the device can support."> <message name="IDS_ASH_STATUS_TRAY_DISPLAY_REMOVED_EXCEEDED_MAXIMUM" desc="The label used when user connects more external displays than the maximum that the device can support.">
This device couldn't support all of your displays, so one has been disconnected This device couldn't support all of your displays, so one has been disconnected
</message> </message>

@@ -0,0 +1 @@
4fe893b2832867a95f7c85a014a56f7712e6929b

@@ -61,6 +61,13 @@ enum ViewID {
// Shown in system tray detailed views: // Shown in system tray detailed views:
VIEW_ID_QS_DETAILED_VIEW_BACK_BUTTON, VIEW_ID_QS_DETAILED_VIEW_BACK_BUTTON,
// QS revamped display detailed view:
VIEW_ID_QS_DISPLAY_MIN,
VIEW_ID_QS_DISPLAY_BRIGHTNESS_SLIDER = VIEW_ID_QS_DISPLAY_MIN,
VIEW_ID_QS_DISPLAY_SCROLL_CONTENT,
VIEW_ID_QS_DISPLAY_TILE_CONTAINER,
VIEW_ID_QS_DISPLAY_MAX = VIEW_ID_QS_DISPLAY_TILE_CONTAINER,
// Status area trays: // Status area trays:
VIEW_ID_SA_MIN, VIEW_ID_SA_MIN,
VIEW_ID_SA_DATE_TRAY = VIEW_ID_SA_MIN, VIEW_ID_SA_DATE_TRAY = VIEW_ID_SA_MIN,

@@ -471,6 +471,7 @@ aggregate_vector_icons("ash_vector_icons") {
"unified_menu_brightness_medium.icon", "unified_menu_brightness_medium.icon",
"unified_menu_cast.icon", "unified_menu_cast.icon",
"unified_menu_dark_mode.icon", "unified_menu_dark_mode.icon",
"unified_menu_dark_mode_off.icon",
"unified_menu_do_not_disturb.icon", "unified_menu_do_not_disturb.icon",
"unified_menu_expand.icon", "unified_menu_expand.icon",
"unified_menu_info.icon", "unified_menu_info.icon",

@@ -0,0 +1,30 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
CANVAS_DIMENSIONS, 20,
MOVE_TO, 3.64f, 5.14f,
LINE_TO, 2, 3.5f,
LINE_TO, 3.24f, 2.26f,
LINE_TO, 18.09f, 17.11f,
LINE_TO, 16.85f, 18.35f,
LINE_TO, 14.86f, 16.36f,
CUBIC_TO, 13.51f, 17.39f, 11.83f, 18, 10, 18,
CUBIC_TO, 5.58f, 18, 2, 14.42f, 2, 10,
CUBIC_TO, 2, 8.17f, 2.61f, 6.49f, 3.64f, 5.14f,
CLOSE,
MOVE_TO, 13.51f, 15.01f,
CUBIC_TO, 12.52f, 15.71f, 11.31f, 16.12f, 10, 16.12f,
V_LINE_TO, 11.5f,
LINE_TO, 13.51f, 15.01f,
CLOSE,
MOVE_TO, 16.12f, 10,
CUBIC_TO, 16.12f, 10.79f, 15.97f, 11.55f, 15.69f, 12.24f,
LINE_TO, 17.11f, 13.66f,
CUBIC_TO, 17.68f, 12.57f, 18, 11.32f, 18, 10,
CUBIC_TO, 18, 5.58f, 14.42f, 2, 10, 2,
CUBIC_TO, 8.68f, 2, 7.43f, 2.32f, 6.34f, 2.89f,
LINE_TO, 10, 6.55f,
V_LINE_TO, 3.88f,
CUBIC_TO, 13.38f, 3.88f, 16.12f, 6.62f, 16.12f, 10,
CLOSE

@@ -0,0 +1,133 @@
// Copyright 2023 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/brightness/display_detailed_view.h"
#include <memory>
#include "ash/public/cpp/ash_view_ids.h"
#include "ash/public/cpp/system_tray_client.h"
#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/brightness/unified_brightness_slider_controller.h"
#include "ash/system/dark_mode/dark_mode_feature_pod_controller.h"
#include "ash/system/model/system_tray_model.h"
#include "ash/system/night_light/night_light_feature_pod_controller.h"
#include "ash/system/status_area_widget.h"
#include "ash/system/tray/detailed_view_delegate.h"
#include "ash/system/tray/tray_popup_utils.h"
#include "ash/system/unified/feature_tile.h"
#include "ash/system/unified/unified_system_tray.h"
#include "ash/system/unified/unified_system_tray_controller.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/views/border.h"
#include "ui/views/controls/scroll_view.h"
#include "ui/views/layout/flex_layout.h"
#include "ui/views/view.h"
#include "ui/views/view_class_properties.h"
namespace ash {
namespace {
constexpr auto kScrollViewMargin = gfx::Insets::TLBR(0, 12, 16, 12);
constexpr auto kTileMargin = gfx::Insets::TLBR(1, 4, 8, 4);
constexpr auto kSliderPadding = gfx::Insets::TLBR(4, 0, 0, 0);
constexpr auto kSliderBorder = gfx::Insets::VH(0, 4);
} // namespace
DisplayDetailedView::DisplayDetailedView(
DetailedViewDelegate* delegate,
UnifiedSystemTrayController* tray_controller)
: TrayDetailedView(delegate),
unified_system_tray_controller_(tray_controller) {
CreateScrollableList();
CreateTitleRow(IDS_ASH_STATUS_TRAY_DISPLAY);
CreateTitleSettingsButton();
// Sets the margin for `ScrollView` to leave some space for the focus ring.
scroller()->SetProperty(views::kMarginsKey, kScrollViewMargin);
auto night_light_controller =
std::make_unique<NightLightFeaturePodController>(
unified_system_tray_controller_);
auto dark_mode_controller = std::make_unique<DarkModeFeaturePodController>(
unified_system_tray_controller_);
auto tile_container = std::make_unique<views::View>();
// Sets the ID for testing.
tile_container->SetID(VIEW_ID_QS_DISPLAY_TILE_CONTAINER);
tile_container->AddChildView(night_light_controller->CreateTile());
tile_container->AddChildView(dark_mode_controller->CreateTile());
// Transfers the ownership so the controllers won't die while the page is
// open.
feature_tile_controllers_.push_back(std::move(night_light_controller));
feature_tile_controllers_.push_back(std::move(dark_mode_controller));
auto* tile_layout =
tile_container->SetLayoutManager(std::make_unique<views::FlexLayout>());
tile_layout->SetDefault(views::kMarginsKey, kTileMargin);
scroll_content()->AddChildView(std::move(tile_container));
brightness_slider_controller_ =
std::make_unique<UnifiedBrightnessSliderController>(
Shell::GetPrimaryRootWindowController()
->GetStatusAreaWidget()
->unified_system_tray()
->model(),
views::Button::PressedCallback());
auto unified_brightness_view =
brightness_slider_controller_->CreateBrightnessSlider();
// Sets the ID for testing.
unified_brightness_view->SetID(VIEW_ID_QS_DISPLAY_BRIGHTNESS_SLIDER);
unified_brightness_view->slider()->SetBorder(
views::CreateEmptyBorder(kSliderBorder));
auto* slider_layout = unified_brightness_view->SetLayoutManager(
std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal, kSliderPadding,
/*between_child_spacing=*/0));
slider_layout->SetFlexForView(unified_brightness_view->slider()->parent(),
/*flex=*/1);
scroll_content()->AddChildView(std::move(unified_brightness_view));
// Sets the ID for testing.
scroll_content()->SetID(VIEW_ID_QS_DISPLAY_SCROLL_CONTENT);
}
DisplayDetailedView::~DisplayDetailedView() = default;
views::View* DisplayDetailedView::GetScrollContentForTest() {
// Provides access to the protected scroll_content() in the base class.
return scroll_content();
}
void DisplayDetailedView::CreateTitleSettingsButton() {
DCHECK(!settings_button_);
tri_view()->SetContainerVisible(TriView::Container::END, /*visible=*/true);
settings_button_ = CreateSettingsButton(
base::BindRepeating(&DisplayDetailedView::OnSettingsClicked,
weak_factory_.GetWeakPtr()),
IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_SETTINGS_TOOLTIP);
settings_button_->SetState(TrayPopupUtils::CanOpenWebUISettings()
? views::Button::STATE_NORMAL
: views::Button::STATE_DISABLED);
tri_view()->AddView(TriView::Container::END, settings_button_);
}
void DisplayDetailedView::OnSettingsClicked() {
if (TrayPopupUtils::CanOpenWebUISettings()) {
CloseBubble();
Shell::Get()->system_tray_model()->client()->ShowDisplaySettings();
}
}
BEGIN_METADATA(DisplayDetailedView, views::View)
END_METADATA
} // namespace ash

@@ -0,0 +1,65 @@
// Copyright 2023 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_BRIGHTNESS_DISPLAY_DETAILED_VIEW_H_
#define ASH_SYSTEM_BRIGHTNESS_DISPLAY_DETAILED_VIEW_H_
#include <memory>
#include "ash/ash_export.h"
#include "ash/system/tray/tray_detailed_view.h"
#include "ui/base/metadata/metadata_header_macros.h"
namespace views {
class View;
} // namespace views
namespace ash {
class UnifiedBrightnessSliderController;
class UnifiedSystemTrayController;
class FeaturePodControllerBase;
// The detailed view to show when the drill-in button next to the brightness
// slider is clicked. This view contains a night light feature tile, a dark mode
// feature tile, and a brightness slider.
class ASH_EXPORT DisplayDetailedView : public TrayDetailedView {
public:
METADATA_HEADER(DisplayDetailedView);
DisplayDetailedView(DetailedViewDelegate* delegate,
UnifiedSystemTrayController* tray_controller);
DisplayDetailedView(const DisplayDetailedView&) = delete;
DisplayDetailedView& operator=(const DisplayDetailedView&) = delete;
~DisplayDetailedView() override;
views::View* GetScrollContentForTest();
private:
// Creates the `settings_button_` on the right end of the title row.
void CreateTitleSettingsButton();
// Callback of the `settings_button_` to open the display system settings
// page.
void OnSettingsClicked();
std::unique_ptr<UnifiedBrightnessSliderController>
brightness_slider_controller_;
UnifiedSystemTrayController* const unified_system_tray_controller_;
// The vector of `FeaturePodControllerBase`. This is needed to store the
// controllers of both tiles so that the controllers exist while the page is
// open.
std::vector<std::unique_ptr<FeaturePodControllerBase>>
feature_tile_controllers_;
// Owned by the views hierarchy.
views::Button* settings_button_ = nullptr;
base::WeakPtrFactory<DisplayDetailedView> weak_factory_{this};
};
} // namespace ash
#endif // ASH_SYSTEM_BRIGHTNESS_DISPLAY_DETAILED_VIEW_H_

@@ -0,0 +1,76 @@
// Copyright 2023 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/brightness/display_detailed_view.h"
#include <memory>
#include <vector>
#include "ash/constants/ash_features.h"
#include "ash/public/cpp/ash_view_ids.h"
#include "ash/system/tray/detailed_view_delegate.h"
#include "ash/system/tray/fake_detailed_view_delegate.h"
#include "ash/test/ash_test_base.h"
#include "base/test/scoped_feature_list.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace {
class DisplayDetailedViewTest : public AshTestBase {
public:
void SetUp() override {
feature_list_.InitWithFeatures({features::kQsRevamp}, {});
AshTestBase::SetUp();
// Create a widget so tests can click on views.
widget_ = CreateFramelessTestWidget();
widget_->SetFullscreen(true);
delegate_ = std::make_unique<FakeDetailedViewDelegate>();
// Passes in a fake delegate and a nullptr as `tray_controller` since we
// don't need to test the actual functionality of controllers.
detailed_view_ =
widget_->SetContentsView(std::make_unique<DisplayDetailedView>(
delegate_.get(), /*tray_controller=*/nullptr));
}
void TearDown() override {
widget_.reset();
detailed_view_ = nullptr;
delegate_.reset();
AshTestBase::TearDown();
}
base::test::ScopedFeatureList feature_list_;
std::unique_ptr<views::Widget> widget_;
std::unique_ptr<DetailedViewDelegate> delegate_;
DisplayDetailedView* detailed_view_ = nullptr;
};
TEST_F(DisplayDetailedViewTest, ScrollContentChildren) {
// The scroll content has two children, one feature tile container and one
// `UnifiedBrightnessView`.
views::View* scroll_content =
detailed_view_->GetViewByID(VIEW_ID_QS_DISPLAY_SCROLL_CONTENT);
ASSERT_TRUE(scroll_content);
ASSERT_EQ(scroll_content->children().size(), 2u);
// The first child of scroll content is the `tile_container`, which has two
// children (night light and dark mode feature tiles).
views::View* tile_container =
scroll_content->GetViewByID(VIEW_ID_QS_DISPLAY_TILE_CONTAINER);
ASSERT_TRUE(tile_container);
ASSERT_EQ(tile_container->children().size(), 2u);
EXPECT_STREQ(tile_container->children()[0]->GetClassName(), "FeatureTile");
EXPECT_STREQ(tile_container->children()[1]->GetClassName(), "FeatureTile");
// The second children of scroll content is the `UnifiedBrightnessView`.
views::View* unified_brightness_view =
scroll_content->GetViewByID(VIEW_ID_QS_DISPLAY_BRIGHTNESS_SLIDER);
EXPECT_STREQ(unified_brightness_view->GetClassName(),
"UnifiedBrightnessView");
}
} // namespace
} // namespace ash

@@ -0,0 +1,35 @@
// Copyright 2023 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/brightness/quick_settings_display_detailed_view_controller.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/brightness/display_detailed_view.h"
#include "ash/system/tray/detailed_view_delegate.h"
#include "ui/base/l10n/l10n_util.h"
namespace ash {
QuickSettingsDisplayDetailedViewController::
QuickSettingsDisplayDetailedViewController(
UnifiedSystemTrayController* tray_controller)
: detailed_view_delegate_(
std::make_unique<DetailedViewDelegate>(tray_controller)),
tray_controller_(tray_controller) {}
QuickSettingsDisplayDetailedViewController::
~QuickSettingsDisplayDetailedViewController() = default;
std::unique_ptr<views::View>
QuickSettingsDisplayDetailedViewController::CreateView() {
return std::make_unique<DisplayDetailedView>(detailed_view_delegate_.get(),
tray_controller_);
}
std::u16string QuickSettingsDisplayDetailedViewController::GetAccessibleName()
const {
return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISPLAY);
}
} // namespace ash

@@ -0,0 +1,44 @@
// Copyright 2023 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_BRIGHTNESS_QUICK_SETTINGS_DISPLAY_DETAILED_VIEW_CONTROLLER_H_
#define ASH_SYSTEM_BRIGHTNESS_QUICK_SETTINGS_DISPLAY_DETAILED_VIEW_CONTROLLER_H_
#include <memory>
#include "ash/ash_export.h"
#include "ash/system/unified/detailed_view_controller.h"
namespace ash {
class DisplayDetailedView;
class DetailedViewDelegate;
class UnifiedSystemTrayController;
// Controller of `DisplayDetailedView` in `UnifiedSystemTray`.
class ASH_EXPORT QuickSettingsDisplayDetailedViewController
: public DetailedViewController {
public:
explicit QuickSettingsDisplayDetailedViewController(
UnifiedSystemTrayController* tray_controller);
QuickSettingsDisplayDetailedViewController(
const QuickSettingsDisplayDetailedViewController&) = delete;
QuickSettingsDisplayDetailedViewController& operator=(
const QuickSettingsDisplayDetailedViewController&) = delete;
~QuickSettingsDisplayDetailedViewController() override;
// DetailedViewController:
std::unique_ptr<views::View> CreateView() override;
std::u16string GetAccessibleName() const override;
private:
const std::unique_ptr<DetailedViewDelegate> detailed_view_delegate_;
UnifiedSystemTrayController* const tray_controller_;
};
} // namespace ash
#endif // ASH_SYSTEM_BRIGHTNESS_QUICK_SETTINGS_DISPLAY_DETAILED_VIEW_CONTROLLER_H_

@@ -4,6 +4,8 @@
#include "ash/system/brightness/unified_brightness_slider_controller.h" #include "ash/system/brightness/unified_brightness_slider_controller.h"
#include <memory>
#include "ash/constants/quick_settings_catalogs.h" #include "ash/constants/quick_settings_catalogs.h"
#include "ash/shell.h" #include "ash/shell.h"
#include "ash/system/brightness/unified_brightness_view.h" #include "ash/system/brightness/unified_brightness_view.h"
@@ -13,16 +15,32 @@
namespace ash { namespace ash {
namespace {
// We don't let the screen brightness go lower than this when it's being
// adjusted via the slider. Otherwise, if the user doesn't know about the
// brightness keys, they may turn the backlight off and not know how to turn
// it back on.
static constexpr double kMinBrightnessPercent = 5.0;
} // namespace
UnifiedBrightnessSliderController::UnifiedBrightnessSliderController( UnifiedBrightnessSliderController::UnifiedBrightnessSliderController(
scoped_refptr<UnifiedSystemTrayModel> model) scoped_refptr<UnifiedSystemTrayModel> model,
: model_(model) {} views::Button::PressedCallback callback)
: model_(model), callback_(callback) {}
UnifiedBrightnessSliderController::~UnifiedBrightnessSliderController() = UnifiedBrightnessSliderController::~UnifiedBrightnessSliderController() =
default; default;
std::unique_ptr<UnifiedBrightnessView>
UnifiedBrightnessSliderController::CreateBrightnessSlider() {
return std::make_unique<UnifiedBrightnessView>(this, model_);
}
views::View* UnifiedBrightnessSliderController::CreateView() { views::View* UnifiedBrightnessSliderController::CreateView() {
DCHECK(!slider_); DCHECK(!slider_);
slider_ = new UnifiedBrightnessView(this, model_); slider_ = new UnifiedBrightnessView(this, model_, callback_);
return slider_; return slider_;
} }
@@ -48,9 +66,6 @@ void UnifiedBrightnessSliderController::SliderValueChanged(
// we don't update the actual brightness. // we don't update the actual brightness.
if (percent < kMinBrightnessPercent && if (percent < kMinBrightnessPercent &&
previous_percent_ < kMinBrightnessPercent) { previous_percent_ < kMinBrightnessPercent) {
// We still need to call `OnDisplayBrightnessChanged()` to update the icon
// of the slider, we just don't update the brightness value.
brightness_control_delegate->SetBrightnessPercent(previous_percent_, true);
return; return;
} }

@@ -5,6 +5,8 @@
#ifndef ASH_SYSTEM_BRIGHTNESS_UNIFIED_BRIGHTNESS_SLIDER_CONTROLLER_H_ #ifndef ASH_SYSTEM_BRIGHTNESS_UNIFIED_BRIGHTNESS_SLIDER_CONTROLLER_H_
#define ASH_SYSTEM_BRIGHTNESS_UNIFIED_BRIGHTNESS_SLIDER_CONTROLLER_H_ #define ASH_SYSTEM_BRIGHTNESS_UNIFIED_BRIGHTNESS_SLIDER_CONTROLLER_H_
#include <memory>
#include "ash/constants/quick_settings_catalogs.h" #include "ash/constants/quick_settings_catalogs.h"
#include "ash/system/unified/unified_slider_view.h" #include "ash/system/unified/unified_slider_view.h"
#include "base/memory/scoped_refptr.h" #include "base/memory/scoped_refptr.h"
@@ -12,12 +14,14 @@
namespace ash { namespace ash {
class UnifiedSystemTrayModel; class UnifiedSystemTrayModel;
class UnifiedBrightnessView;
// Controller of a slider that can change display brightness. // Controller of a slider that can change display brightness.
class UnifiedBrightnessSliderController : public UnifiedSliderListener { class ASH_EXPORT UnifiedBrightnessSliderController
: public UnifiedSliderListener {
public: public:
explicit UnifiedBrightnessSliderController( UnifiedBrightnessSliderController(scoped_refptr<UnifiedSystemTrayModel> model,
scoped_refptr<UnifiedSystemTrayModel> model); views::Button::PressedCallback callback);
UnifiedBrightnessSliderController(const UnifiedBrightnessSliderController&) = UnifiedBrightnessSliderController(const UnifiedBrightnessSliderController&) =
delete; delete;
@@ -26,6 +30,10 @@ class UnifiedBrightnessSliderController : public UnifiedSliderListener {
~UnifiedBrightnessSliderController() override; ~UnifiedBrightnessSliderController() override;
// For QsRevamp: Creates a slider view for the brightness slider in
// `DisplayDetailedView`.
std::unique_ptr<UnifiedBrightnessView> CreateBrightnessSlider();
// UnifiedSliderListener: // UnifiedSliderListener:
views::View* CreateView() override; views::View* CreateView() override;
QsSliderCatalogName GetCatalogName() override; QsSliderCatalogName GetCatalogName() override;
@@ -34,14 +42,9 @@ class UnifiedBrightnessSliderController : public UnifiedSliderListener {
float old_value, float old_value,
views::SliderChangeReason reason) override; views::SliderChangeReason reason) override;
// We don't let the screen brightness go lower than this when it's being
// adjusted via the slider. Otherwise, if the user doesn't know about the
// brightness keys, they may turn the backlight off and not know how to turn
// it back on.
static constexpr double kMinBrightnessPercent = 5.0;
private: private:
scoped_refptr<UnifiedSystemTrayModel> model_; scoped_refptr<UnifiedSystemTrayModel> model_;
views::Button::PressedCallback const callback_;
UnifiedSliderView* slider_ = nullptr; UnifiedSliderView* slider_ = nullptr;
// We have to store previous manually set value because |old_value| might be // We have to store previous manually set value because |old_value| might be

@@ -4,16 +4,22 @@
#include "ash/system/brightness/unified_brightness_view.h" #include "ash/system/brightness/unified_brightness_view.h"
#include <memory>
#include "ash/constants/ash_features.h" #include "ash/constants/ash_features.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h" #include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_id.h" #include "ash/style/ash_color_id.h"
#include "ash/system/brightness/unified_brightness_slider_controller.h" #include "ash/system/brightness/unified_brightness_slider_controller.h"
#include "ash/system/night_light/night_light_controller_impl.h"
#include "ash/system/tray/tray_constants.h" #include "ash/system/tray/tray_constants.h"
#include "base/functional/bind.h"
#include "base/memory/scoped_refptr.h" #include "base/memory/scoped_refptr.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h" #include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/models/image_model.h" #include "ui/base/models/image_model.h"
#include "ui/chromeos/styles/cros_tokens_color_mappings.h" #include "ui/chromeos/styles/cros_tokens_color_mappings.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/gfx/vector_icon_types.h" #include "ui/gfx/vector_icon_types.h"
namespace ash { namespace ash {
@@ -36,25 +42,42 @@ const gfx::VectorIcon& GetBrightnessIconForLevel(float level) {
UnifiedBrightnessView::UnifiedBrightnessView( UnifiedBrightnessView::UnifiedBrightnessView(
UnifiedBrightnessSliderController* controller, UnifiedBrightnessSliderController* controller,
scoped_refptr<UnifiedSystemTrayModel> model) scoped_refptr<UnifiedSystemTrayModel> model,
absl::optional<views::Button::PressedCallback> detailed_button_callback)
: UnifiedSliderView(views::Button::PressedCallback(), : UnifiedSliderView(views::Button::PressedCallback(),
controller, controller,
kUnifiedMenuBrightnessIcon, kUnifiedMenuBrightnessIcon,
IDS_ASH_STATUS_TRAY_BRIGHTNESS), IDS_ASH_STATUS_TRAY_BRIGHTNESS),
model_(model), model_(model),
controller_(controller) { night_light_controller_(Shell::Get()->night_light_controller()) {
model_->AddObserver(this);
if (features::IsQsRevampEnabled()) { if (features::IsQsRevampEnabled()) {
AddChildView(std::make_unique<IconButton>( // For QsRevamp: This case applies to the brightness slider in the
views::Button::PressedCallback(), IconButton::Type::kMedium, // `DisplayDetailedView`. If `detailed_button_callback` is not passed in,
&kUnifiedMenuNightLightOffIcon, // both the `night_light_button_` and the drill-in button will not be added.
IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_BUTTON_LABEL, if (!detailed_button_callback.has_value()) {
OnDisplayBrightnessChanged(/*by_user=*/false);
return;
}
const bool enabled = night_light_controller_->GetEnabled();
night_light_button_ = AddChildView(std::make_unique<IconButton>(
base::BindRepeating(&UnifiedBrightnessView::OnNightLightButtonPressed,
base::Unretained(this)),
IconButton::Type::kMedium,
enabled ? &kUnifiedMenuNightLightIcon : &kUnifiedMenuNightLightOffIcon,
l10n_util::GetStringFUTF16(
IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_TOGGLE_TOOLTIP,
l10n_util::GetStringUTF16(
enabled
? IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_ENABLED_STATE_TOOLTIP
: IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_DISABLED_STATE_TOOLTIP)),
/*is_togglable=*/true, /*is_togglable=*/true,
/*has_border=*/true)); /*has_border=*/true));
AddChildView(std::make_unique<IconButton>( AddChildView(std::make_unique<IconButton>(
views::Button::PressedCallback(), std::move(detailed_button_callback.value()),
features::IsQsRevampEnabled() ? IconButton::Type::kMediumFloating IconButton::Type::kMediumFloating, &kQuickSettingsRightArrowIcon,
: IconButton::Type::kMedium,
&kQuickSettingsRightArrowIcon,
IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_SETTINGS_TOOLTIP)); IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_SETTINGS_TOOLTIP));
} else { } else {
button()->SetEnabled(false); button()->SetEnabled(false);
@@ -65,9 +88,7 @@ UnifiedBrightnessView::UnifiedBrightnessView(
ui::ImageModel::FromVectorIcon(kUnifiedMenuBrightnessIcon, ui::ImageModel::FromVectorIcon(kUnifiedMenuBrightnessIcon,
kColorAshButtonIconColor)); kColorAshButtonIconColor));
} }
OnDisplayBrightnessChanged(/*by_user=*/false);
model_->AddObserver(this);
OnDisplayBrightnessChanged(false /* by_user */);
} }
UnifiedBrightnessView::~UnifiedBrightnessView() { UnifiedBrightnessView::~UnifiedBrightnessView() {
@@ -76,25 +97,45 @@ UnifiedBrightnessView::~UnifiedBrightnessView() {
void UnifiedBrightnessView::OnDisplayBrightnessChanged(bool by_user) { void UnifiedBrightnessView::OnDisplayBrightnessChanged(bool by_user) {
float level = model_->display_brightness(); float level = model_->display_brightness();
float slider_level = slider()->GetValue();
// If level is less than `kMinBrightnessPercent`, use the slider value as
// `level` so that when the slider is at 0 point, the icon for the slider is
// `kUnifiedMenuBrightnessLowIcon`. Otherwise `level` will remain to be
// `kMinBrightnessPercent` and the icon cannot be updated.
if (level * 100 <= controller_->kMinBrightnessPercent) {
level = slider_level;
}
if (features::IsQsRevampEnabled()) { if (features::IsQsRevampEnabled()) {
slider_icon()->SetImage(ui::ImageModel::FromVectorIcon( slider_icon()->SetImage(ui::ImageModel::FromVectorIcon(
GetBrightnessIconForLevel(level), GetBrightnessIconForLevel(level),
cros_tokens::kCrosSysSystemOnPrimaryContainer, kQsSliderIconSize)); cros_tokens::kCrosSysSystemOnPrimaryContainer, kQsSliderIconSize));
} }
SetSliderValue(level, by_user); SetSliderValue(level, by_user);
} }
void UnifiedBrightnessView::OnNightLightButtonPressed() {
night_light_controller_->Toggle();
UpdateNightLightButton();
}
void UnifiedBrightnessView::UpdateNightLightButton() {
const bool enabled = night_light_controller_->GetEnabled();
// Updates the icon of `night_light_button_`.
night_light_button_->SetVectorIcon(enabled ? kUnifiedMenuNightLightIcon
: kUnifiedMenuNightLightOffIcon);
// Updates the tooltip of `night_light_button_`.
std::u16string toggle_tooltip = l10n_util::GetStringUTF16(
enabled ? IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_ENABLED_STATE_TOOLTIP
: IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_DISABLED_STATE_TOOLTIP);
night_light_button_->SetTooltipText(l10n_util::GetStringFUTF16(
IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_TOGGLE_TOOLTIP, toggle_tooltip));
}
void UnifiedBrightnessView::VisibilityChanged(View* starting_from,
bool is_visible) {
OnDisplayBrightnessChanged(/*by_user=*/false);
// Only updates the `night_light_button_` if in the main page.
if (night_light_button_) {
UpdateNightLightButton();
}
}
BEGIN_METADATA(UnifiedBrightnessView, views::View) BEGIN_METADATA(UnifiedBrightnessView, views::View)
END_METADATA END_METADATA

@@ -7,15 +7,14 @@
#include "ash/ash_export.h" #include "ash/ash_export.h"
#include "ash/resources/vector_icons/vector_icons.h" #include "ash/resources/vector_icons/vector_icons.h"
#include "ash/system/brightness/unified_brightness_slider_controller.h"
#include "ash/system/night_light/night_light_controller_impl.h"
#include "ash/system/unified/unified_slider_view.h" #include "ash/system/unified/unified_slider_view.h"
#include "ash/system/unified/unified_system_tray_model.h" #include "ash/system/unified/unified_system_tray_model.h"
#include "base/memory/scoped_refptr.h" #include "base/memory/scoped_refptr.h"
#include "ui/base/metadata/metadata_header_macros.h" #include "ui/base/metadata/metadata_header_macros.h"
namespace ash { namespace ash {
class UnifiedBrightnessSliderController;
// View of a slider that can change display brightness. It observes current // View of a slider that can change display brightness. It observes current
// brightness level from UnifiedSystemTrayModel. // brightness level from UnifiedSystemTrayModel.
class ASH_EXPORT UnifiedBrightnessView class ASH_EXPORT UnifiedBrightnessView
@@ -25,11 +24,11 @@ class ASH_EXPORT UnifiedBrightnessView
METADATA_HEADER(UnifiedBrightnessView); METADATA_HEADER(UnifiedBrightnessView);
UnifiedBrightnessView(UnifiedBrightnessSliderController* controller, UnifiedBrightnessView(UnifiedBrightnessSliderController* controller,
scoped_refptr<UnifiedSystemTrayModel> model); scoped_refptr<UnifiedSystemTrayModel> model,
absl::optional<views::Button::PressedCallback>
detailed_button_callback = absl::nullopt);
UnifiedBrightnessView(const UnifiedBrightnessView&) = delete; UnifiedBrightnessView(const UnifiedBrightnessView&) = delete;
UnifiedBrightnessView& operator=(const UnifiedBrightnessView&) = delete; UnifiedBrightnessView& operator=(const UnifiedBrightnessView&) = delete;
~UnifiedBrightnessView() override; ~UnifiedBrightnessView() override;
// UnifiedSystemTrayModel::Observer: // UnifiedSystemTrayModel::Observer:
@@ -45,8 +44,19 @@ class ASH_EXPORT UnifiedBrightnessView
}; };
private: private:
// Callback called when `night_light_button_` is pressed.
void OnNightLightButtonPressed();
// Updates the icon and tooltip of `night_light_button_`.
void UpdateNightLightButton();
// UnifiedSliderView::
void VisibilityChanged(View* starting_from, bool is_visible) override;
scoped_refptr<UnifiedSystemTrayModel> model_; scoped_refptr<UnifiedSystemTrayModel> model_;
UnifiedBrightnessSliderController* const controller_; NightLightControllerImpl* const night_light_controller_;
// Owned by the views hierarchy.
IconButton* night_light_button_ = nullptr;
}; };
} // namespace ash } // namespace ash

@@ -36,12 +36,15 @@ class UnifiedBrightnessViewTest : public AshTestBase {
controller()->brightness_slider_controller_.get(); controller()->brightness_slider_controller_.get();
unified_brightness_view_ = static_cast<UnifiedBrightnessView*>( unified_brightness_view_ = static_cast<UnifiedBrightnessView*>(
controller()->unified_brightness_view_); controller()->unified_brightness_view_);
brightness_slider_ =
brightness_slider_controller_->CreateBrightnessSlider();
} }
void TearDown() override { void TearDown() override {
// Tests the environment tears down with the bubble closed. // Tests the environment tears down with the bubble closed.
// In `UnifiedVolumeViewTest`, the environment is torn down with the bubble // In `UnifiedVolumeViewTest`, the environment is torn down with the bubble
// open, so we can test both cases. // open, so we can test both cases.
brightness_slider_ = nullptr;
GetPrimaryUnifiedSystemTray()->CloseBubble(); GetPrimaryUnifiedSystemTray()->CloseBubble();
AshTestBase::TearDown(); AshTestBase::TearDown();
} }
@@ -59,6 +62,10 @@ class UnifiedBrightnessViewTest : public AshTestBase {
return unified_brightness_view_; return unified_brightness_view_;
} }
UnifiedBrightnessView* brightness_slider() {
return brightness_slider_.get();
}
views::Slider* slider() { return unified_brightness_view_->slider(); } views::Slider* slider() { return unified_brightness_view_->slider(); }
views::ImageView* slider_icon() { views::ImageView* slider_icon() {
@@ -72,7 +79,13 @@ class UnifiedBrightnessViewTest : public AshTestBase {
} }
private: private:
// The `UnifiedBrightnessView` containing a `QuickSettingsSlider`, a
// `NightLight` button, and a drill-in button.
UnifiedBrightnessView* unified_brightness_view_ = nullptr; UnifiedBrightnessView* unified_brightness_view_ = nullptr;
// The `UnifiedBrightnessView` containing only a `QuickSettingsSlider`.
std::unique_ptr<UnifiedBrightnessView> brightness_slider_ = nullptr;
UnifiedBrightnessSliderController* brightness_slider_controller_ = nullptr; UnifiedBrightnessSliderController* brightness_slider_controller_ = nullptr;
base::test::ScopedFeatureList feature_list_; base::test::ScopedFeatureList feature_list_;
}; };
@@ -80,6 +93,7 @@ class UnifiedBrightnessViewTest : public AshTestBase {
// Tests that `UnifiedBrightnessView` is made up of a `QuickSettingsSlider`, a // Tests that `UnifiedBrightnessView` is made up of a `QuickSettingsSlider`, a
// `NightLight` button, and a drill-in button that leads to the display subpage. // `NightLight` button, and a drill-in button that leads to the display subpage.
TEST_F(UnifiedBrightnessViewTest, SliderButtonComponents) { TEST_F(UnifiedBrightnessViewTest, SliderButtonComponents) {
EXPECT_EQ(unified_brightness_view()->children().size(), 3u);
EXPECT_STREQ( EXPECT_STREQ(
unified_brightness_view()->children()[0]->children()[0]->GetClassName(), unified_brightness_view()->children()[0]->children()[0]->GetClassName(),
"QuickSettingsSlider"); "QuickSettingsSlider");
@@ -88,10 +102,13 @@ TEST_F(UnifiedBrightnessViewTest, SliderButtonComponents) {
auto* night_light_button = auto* night_light_button =
static_cast<IconButton*>(unified_brightness_view()->children()[1]); static_cast<IconButton*>(unified_brightness_view()->children()[1]);
EXPECT_STREQ(night_light_button->GetClassName(), "IconButton"); EXPECT_STREQ(night_light_button->GetClassName(), "IconButton");
EXPECT_EQ( EXPECT_EQ(night_light_button->GetAccessibleName(),
night_light_button->GetAccessibleName(), l10n_util::GetStringFUTF16(
l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_BUTTON_LABEL)); IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_TOGGLE_TOOLTIP,
EXPECT_EQ(night_light_button->GetTooltipText(), u"Night Light"); l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_DISABLED_STATE_TOOLTIP)));
EXPECT_EQ(night_light_button->GetTooltipText(),
u"Toggle Night Light. Night Light is off.");
auto* display_subpage_drill_in_button = auto* display_subpage_drill_in_button =
static_cast<IconButton*>(unified_brightness_view()->children()[2]); static_cast<IconButton*>(unified_brightness_view()->children()[2]);
@@ -106,6 +123,15 @@ TEST_F(UnifiedBrightnessViewTest, SliderButtonComponents) {
// drill-in button. // drill-in button.
} }
// Tests that `UnifiedBrightnessView` in the display subpage is made up of a
// `QuickSettingsSlider`.
TEST_F(UnifiedBrightnessViewTest, SliderComponent) {
EXPECT_EQ(brightness_slider()->children().size(), 1u);
EXPECT_STREQ(
brightness_slider()->children()[0]->children()[0]->GetClassName(),
"QuickSettingsSlider");
}
// Tests the slider icon matches the slider level. // Tests the slider icon matches the slider level.
TEST_F(UnifiedBrightnessViewTest, SliderIcon) { TEST_F(UnifiedBrightnessViewTest, SliderIcon) {
const float levels[] = {0.0, 0.04, 0.2, 0.25, 0.49, 0.5, 0.7, 0.75, 0.9, 1}; const float levels[] = {0.0, 0.04, 0.2, 0.25, 0.49, 0.5, 0.7, 0.75, 0.9, 1};
@@ -126,8 +152,10 @@ TEST_F(UnifiedBrightnessViewTest, SliderIcon) {
slider_icon()->GetImageModel().GetVectorIcon().vector_icon(); slider_icon()->GetImageModel().GetVectorIcon().vector_icon();
if (level <= 0.0) { if (level <= 0.0) {
// The minimum level for brightness is 0.05, since `SliderValueChanged()`
// will adjust the brightness level and set the icon accordingly.
EXPECT_STREQ(icon->name, EXPECT_STREQ(icon->name,
UnifiedBrightnessView::kBrightnessLevelIcons[0]->name); UnifiedBrightnessView::kBrightnessLevelIcons[1]->name);
} else if (level <= 0.5) { } else if (level <= 0.5) {
EXPECT_STREQ(icon->name, EXPECT_STREQ(icon->name,
UnifiedBrightnessView::kBrightnessLevelIcons[1]->name); UnifiedBrightnessView::kBrightnessLevelIcons[1]->name);

@@ -4,6 +4,7 @@
#include "ash/system/dark_mode/dark_mode_feature_pod_controller.h" #include "ash/system/dark_mode/dark_mode_feature_pod_controller.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/quick_settings_catalogs.h" #include "ash/constants/quick_settings_catalogs.h"
#include "ash/public/cpp/system_tray_client.h" #include "ash/public/cpp/system_tray_client.h"
#include "ash/resources/vector_icons/vector_icons.h" #include "ash/resources/vector_icons/vector_icons.h"
@@ -14,16 +15,28 @@
#include "ash/system/model/system_tray_model.h" #include "ash/system/model/system_tray_model.h"
#include "ash/system/tray/tray_popup_utils.h" #include "ash/system/tray/tray_popup_utils.h"
#include "ash/system/unified/feature_pod_button.h" #include "ash/system/unified/feature_pod_button.h"
#include "ash/system/unified/feature_tile.h"
#include "ash/system/unified/quick_settings_metrics_util.h" #include "ash/system/unified/quick_settings_metrics_util.h"
#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_functions.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
namespace ash { namespace ash {
namespace {
bool IsVisible() {
// TODO(minch): Add the logic for login screen.
// Disable dark mode feature pod in OOBE since only light mode should be
// allowed there.
return Shell::Get()->session_controller()->IsActiveUserSessionStarted() &&
Shell::Get()->session_controller()->GetSessionState() !=
session_manager::SessionState::OOBE;
}
} // namespace
DarkModeFeaturePodController::DarkModeFeaturePodController( DarkModeFeaturePodController::DarkModeFeaturePodController(
UnifiedSystemTrayController* tray_controller) UnifiedSystemTrayController* tray_controller) {
: tray_controller_(tray_controller) {
DCHECK(tray_controller_);
DarkLightModeControllerImpl::Get()->AddObserver(this); DarkLightModeControllerImpl::Get()->AddObserver(this);
} }
@@ -38,13 +51,7 @@ FeaturePodButton* DarkModeFeaturePodController::CreateButton() {
button_->SetLabel(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DARK_THEME)); button_->SetLabel(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DARK_THEME));
button_->SetLabelTooltip(l10n_util::GetStringUTF16( button_->SetLabelTooltip(l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_DARK_THEME_SETTINGS_TOOLTIP)); IDS_ASH_STATUS_TRAY_DARK_THEME_SETTINGS_TOOLTIP));
// TODO(minch): Add the logic for login screen. const bool visible = IsVisible();
// Disable dark mode feature pod in OOBE since only light mode should be
// allowed there.
const bool visible =
Shell::Get()->session_controller()->IsActiveUserSessionStarted() &&
Shell::Get()->session_controller()->GetSessionState() !=
session_manager::SessionState::OOBE;
if (visible) if (visible)
TrackVisibilityUMA(); TrackVisibilityUMA();
button_->SetVisible(visible); button_->SetVisible(visible);
@@ -53,6 +60,26 @@ FeaturePodButton* DarkModeFeaturePodController::CreateButton() {
return button_; return button_;
} }
std::unique_ptr<FeatureTile> DarkModeFeaturePodController::CreateTile(
bool compact) {
DCHECK(features::IsQsRevampEnabled());
DCHECK(!tile_);
auto tile = std::make_unique<FeatureTile>(
base::BindRepeating(&DarkModeFeaturePodController::OnIconPressed,
weak_ptr_factory_.GetWeakPtr()));
tile_ = tile.get();
tile_->SetVectorIcon(kUnifiedMenuDarkModeIcon);
tile_->SetLabel(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DARK_THEME));
const bool visible = IsVisible();
if (visible) {
TrackVisibilityUMA();
}
tile_->SetVisible(visible);
UpdateTile(DarkLightModeControllerImpl::Get()->IsDarkModeEnabled());
return tile;
}
QsFeatureCatalogName DarkModeFeaturePodController::GetCatalogName() { QsFeatureCatalogName DarkModeFeaturePodController::GetCatalogName() {
return QsFeatureCatalogName::kDarkMode; return QsFeatureCatalogName::kDarkMode;
} }
@@ -73,11 +100,18 @@ void DarkModeFeaturePodController::OnIconPressed() {
} }
void DarkModeFeaturePodController::OnLabelPressed() { void DarkModeFeaturePodController::OnLabelPressed() {
if (features::IsQsRevampEnabled()) {
return;
}
TrackDiveInUMA(); TrackDiveInUMA();
Shell::Get()->system_tray_model()->client()->ShowDarkModeSettings(); Shell::Get()->system_tray_model()->client()->ShowDarkModeSettings();
} }
void DarkModeFeaturePodController::OnColorModeChanged(bool dark_mode_enabled) { void DarkModeFeaturePodController::OnColorModeChanged(bool dark_mode_enabled) {
if (features::IsQsRevampEnabled()) {
UpdateTile(dark_mode_enabled);
return;
}
UpdateButton(dark_mode_enabled); UpdateButton(dark_mode_enabled);
} }
@@ -104,4 +138,27 @@ void DarkModeFeaturePodController::UpdateButton(bool dark_mode_enabled) {
IDS_ASH_STATUS_TRAY_DARK_THEME_TOGGLE_TOOLTIP, tooltip_state)); IDS_ASH_STATUS_TRAY_DARK_THEME_TOGGLE_TOOLTIP, tooltip_state));
} }
void DarkModeFeaturePodController::UpdateTile(bool dark_mode_enabled) {
tile_->SetToggled(dark_mode_enabled);
if (Shell::Get()->dark_light_mode_controller()->GetAutoScheduleEnabled()) {
tile_->SetSubLabel(l10n_util::GetStringUTF16(
dark_mode_enabled
? IDS_ASH_STATUS_TRAY_DARK_THEME_ON_STATE_AUTO_SCHEDULED
: IDS_ASH_STATUS_TRAY_DARK_THEME_OFF_STATE_AUTO_SCHEDULED));
} else {
tile_->SetSubLabel(l10n_util::GetStringUTF16(
dark_mode_enabled ? IDS_ASH_STATUS_TRAY_DARK_THEME_ON_STATE
: IDS_ASH_STATUS_TRAY_DARK_THEME_OFF_STATE));
}
std::u16string tooltip_state = l10n_util::GetStringUTF16(
dark_mode_enabled
? IDS_ASH_STATUS_TRAY_DARK_THEME_ENABLED_STATE_TOOLTIP
: IDS_ASH_STATUS_TRAY_DARK_THEME_DISABLED_STATE_TOOLTIP);
tile_->SetTooltipText(l10n_util::GetStringFUTF16(
IDS_ASH_STATUS_TRAY_DARK_THEME_TOGGLE_TOOLTIP, tooltip_state));
tile_->SetVectorIcon(dark_mode_enabled ? kUnifiedMenuDarkModeIcon
: kUnifiedMenuDarkModeOffIcon);
}
} // namespace ash } // namespace ash

@@ -28,6 +28,7 @@ class ASH_EXPORT DarkModeFeaturePodController : public FeaturePodControllerBase,
// FeaturePodControllerBase: // FeaturePodControllerBase:
FeaturePodButton* CreateButton() override; FeaturePodButton* CreateButton() override;
std::unique_ptr<FeatureTile> CreateTile(bool compact = false) override;
QsFeatureCatalogName GetCatalogName() override; QsFeatureCatalogName GetCatalogName() override;
void OnIconPressed() override; void OnIconPressed() override;
void OnLabelPressed() override; void OnLabelPressed() override;
@@ -38,9 +39,14 @@ class ASH_EXPORT DarkModeFeaturePodController : public FeaturePodControllerBase,
private: private:
void UpdateButton(bool dark_mode_enabled); void UpdateButton(bool dark_mode_enabled);
UnifiedSystemTrayController* const tray_controller_; // For QsRevamp:
void UpdateTile(bool dark_mode_enabled);
// Owned by the views hierarchy.
FeaturePodButton* button_ = nullptr; FeaturePodButton* button_ = nullptr;
FeatureTile* tile_ = nullptr;
base::WeakPtrFactory<DarkModeFeaturePodController> weak_ptr_factory_{this};
}; };
} // namespace ash } // namespace ash

@@ -9,6 +9,7 @@
#include "ash/shell.h" #include "ash/shell.h"
#include "ash/style/dark_light_mode_controller_impl.h" #include "ash/style/dark_light_mode_controller_impl.h"
#include "ash/system/unified/feature_pod_button.h" #include "ash/system/unified/feature_pod_button.h"
#include "ash/system/unified/feature_tile.h"
#include "ash/system/unified/unified_system_tray.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_bubble.h"
#include "ash/test/ash_test_base.h" #include "ash/test/ash_test_base.h"
@@ -18,38 +19,101 @@
namespace ash { namespace ash {
using DarkModeFeaturePodControllerTest = AshTestBase; // Tests are parameterized by feature QsRevamp.
class DarkModeFeaturePodControllerTest
: public AshTestBase,
public testing::WithParamInterface<bool> {
public:
bool IsQsRevampEnabled() const { return GetParam(); }
// AshTestBase:
void SetUp() override {
if (IsQsRevampEnabled()) {
feature_list_.InitWithFeatures(
{chromeos::features::kDarkLightMode, features::kQsRevamp}, {});
} else {
feature_list_.InitWithFeatures({chromeos::features::kDarkLightMode},
{features::kQsRevamp});
}
AshTestBase::SetUp();
system_tray_ = GetPrimaryUnifiedSystemTray();
system_tray_->ShowBubble();
feature_pod_controller_ = std::make_unique<DarkModeFeaturePodController>(
system_tray_->bubble()->unified_system_tray_controller());
}
void TearDown() override {
tile_.reset();
button_.reset();
feature_pod_controller_.reset();
system_tray_->CloseBubble();
AshTestBase::TearDown();
}
void CreateButton() {
if (IsQsRevampEnabled()) {
tile_ = feature_pod_controller_->CreateTile();
} else {
button_ = base::WrapUnique(feature_pod_controller_->CreateButton());
}
}
bool IsButtonVisible() {
return IsQsRevampEnabled() ? tile_->GetVisible() : button_->GetVisible();
}
bool IsButtonToggled() {
return IsQsRevampEnabled() ? tile_->IsToggled() : button_->IsToggled();
}
void PressIcon() { feature_pod_controller_->OnIconPressed(); }
void PressLabel() { feature_pod_controller_->OnLabelPressed(); }
private:
base::test::ScopedFeatureList feature_list_;
std::unique_ptr<DarkModeFeaturePodController> feature_pod_controller_;
std::unique_ptr<FeaturePodButton> button_;
std::unique_ptr<FeatureTile> tile_;
UnifiedSystemTray* system_tray_ = nullptr;
};
INSTANTIATE_TEST_SUITE_P(QsRevamp,
DarkModeFeaturePodControllerTest,
testing::Bool());
// Tests that toggling dark mode from the system tray disables auto scheduling // Tests that toggling dark mode from the system tray disables auto scheduling
// and switches the color mode properly. // and switches the color mode properly.
TEST_F(DarkModeFeaturePodControllerTest, ToggleDarkMode) { TEST_P(DarkModeFeaturePodControllerTest, ToggleDarkMode) {
base::test::ScopedFeatureList feature_list; CreateButton();
feature_list.InitAndEnableFeature(chromeos::features::kDarkLightMode); EXPECT_TRUE(IsButtonVisible());
auto* dark_light_mode_controller = DarkLightModeControllerImpl::Get(); auto* dark_light_mode_controller = DarkLightModeControllerImpl::Get();
dark_light_mode_controller->OnActiveUserPrefServiceChanged( dark_light_mode_controller->OnActiveUserPrefServiceChanged(
Shell::Get()->session_controller()->GetActivePrefService()); Shell::Get()->session_controller()->GetActivePrefService());
UnifiedSystemTray* system_tray = GetPrimaryUnifiedSystemTray();
system_tray->ShowBubble();
std::unique_ptr<DarkModeFeaturePodController>
dark_mode_feature_pod_controller =
std::make_unique<DarkModeFeaturePodController>(
system_tray->bubble()->unified_system_tray_controller());
std::unique_ptr<FeaturePodButton> button(
dark_mode_feature_pod_controller->CreateButton());
// No metrics logged before clicking on any views. // No metrics logged before clicking on any views.
auto histogram_tester = std::make_unique<base::HistogramTester>(); auto histogram_tester = std::make_unique<base::HistogramTester>();
histogram_tester->ExpectTotalCount( if (IsQsRevampEnabled()) {
"Ash.UnifiedSystemView.FeaturePod.ToggledOn", histogram_tester->ExpectTotalCount("Ash.QuickSettings.FeaturePod.ToggledOn",
/*count=*/0); /*expected_count=*/0);
histogram_tester->ExpectTotalCount( histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOff", "Ash.QuickSettings.FeaturePod.ToggledOff",
/*count=*/0); /*expected_count=*/0);
histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn", histogram_tester->ExpectTotalCount("Ash.QuickSettings.FeaturePod.DiveIn",
/*count=*/0); /*expected_count=*/0);
} else {
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOn",
/*expected_count=*/0);
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOff",
/*expected_count=*/0);
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.DiveIn",
/*expected_count=*/0);
}
// Enable dark mode auto scheduling. // Enable dark mode auto scheduling.
auto* controller = Shell::Get()->dark_light_mode_controller(); auto* controller = Shell::Get()->dark_light_mode_controller();
@@ -58,7 +122,7 @@ TEST_F(DarkModeFeaturePodControllerTest, ToggleDarkMode) {
// Check that the statuses of toggle and dark mode are consistent. // Check that the statuses of toggle and dark mode are consistent.
bool dark_mode_enabled = dark_light_mode_controller->IsDarkModeEnabled(); bool dark_mode_enabled = dark_light_mode_controller->IsDarkModeEnabled();
EXPECT_EQ(dark_mode_enabled, button->IsToggled()); EXPECT_EQ(dark_mode_enabled, IsButtonToggled());
// Set the init state to enabled. // Set the init state to enabled.
if (!dark_mode_enabled) if (!dark_mode_enabled)
@@ -66,56 +130,101 @@ TEST_F(DarkModeFeaturePodControllerTest, ToggleDarkMode) {
// Pressing the dark mode button should disable the scheduling and switch the // Pressing the dark mode button should disable the scheduling and switch the
// dark mode status. // dark mode status.
dark_mode_feature_pod_controller->OnIconPressed(); PressIcon();
EXPECT_FALSE(controller->GetAutoScheduleEnabled()); EXPECT_FALSE(controller->GetAutoScheduleEnabled());
EXPECT_EQ(false, dark_light_mode_controller->IsDarkModeEnabled()); EXPECT_EQ(false, dark_light_mode_controller->IsDarkModeEnabled());
EXPECT_EQ(false, button->IsToggled()); EXPECT_EQ(false, IsButtonToggled());
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOn",
/*count=*/0);
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOff",
/*count=*/1);
histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
/*count=*/0);
histogram_tester->ExpectBucketCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOff",
QsFeatureCatalogName::kDarkMode,
/*expected_count=*/1);
// Pressing the dark mode button again should only switch the dark mode status if (IsQsRevampEnabled()) {
// while maintaining the disabled status of scheduling. histogram_tester->ExpectTotalCount("Ash.QuickSettings.FeaturePod.ToggledOn",
dark_mode_feature_pod_controller->OnIconPressed(); /*expected_count=*/0);
histogram_tester->ExpectTotalCount(
"Ash.QuickSettings.FeaturePod.ToggledOff",
/*expected_count=*/1);
histogram_tester->ExpectTotalCount("Ash.QuickSettings.FeaturePod.DiveIn",
/*expected_count=*/0);
histogram_tester->ExpectBucketCount(
"Ash.QuickSettings.FeaturePod.ToggledOff",
QsFeatureCatalogName::kDarkMode,
/*expected_count=*/1);
} else {
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOn",
/*expected_count=*/0);
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOff",
/*expected_count=*/1);
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.DiveIn",
/*expected_count=*/0);
histogram_tester->ExpectBucketCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOff",
QsFeatureCatalogName::kDarkMode,
/*expected_count=*/1);
}
// Pressing the dark mode button again should only switch the dark mode
// status while maintaining the disabled status of scheduling.
PressIcon();
EXPECT_FALSE(controller->GetAutoScheduleEnabled()); EXPECT_FALSE(controller->GetAutoScheduleEnabled());
EXPECT_EQ(true, dark_light_mode_controller->IsDarkModeEnabled()); EXPECT_EQ(true, dark_light_mode_controller->IsDarkModeEnabled());
EXPECT_EQ(true, button->IsToggled()); EXPECT_EQ(true, IsButtonToggled());
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOn",
/*count=*/1);
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOff",
/*count=*/1);
histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn",
/*count=*/0);
histogram_tester->ExpectBucketCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOn",
QsFeatureCatalogName::kDarkMode,
/*expected_count=*/1);
dark_mode_feature_pod_controller->OnLabelPressed(); if (IsQsRevampEnabled()) {
histogram_tester->ExpectTotalCount( histogram_tester->ExpectTotalCount("Ash.QuickSettings.FeaturePod.ToggledOn",
"Ash.UnifiedSystemView.FeaturePod.ToggledOn", /*expected_count=*/1);
/*count=*/1); histogram_tester->ExpectTotalCount(
histogram_tester->ExpectTotalCount( "Ash.QuickSettings.FeaturePod.ToggledOff",
"Ash.UnifiedSystemView.FeaturePod.ToggledOff", /*expected_count=*/1);
/*count=*/1); histogram_tester->ExpectTotalCount("Ash.QuickSettings.FeaturePod.DiveIn",
histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn", /*expected_count=*/0);
/*count=*/1); histogram_tester->ExpectBucketCount(
histogram_tester->ExpectBucketCount("Ash.UnifiedSystemView.FeaturePod.DiveIn", "Ash.QuickSettings.FeaturePod.ToggledOn",
QsFeatureCatalogName::kDarkMode, QsFeatureCatalogName::kDarkMode,
/*expected_count=*/1); /*expected_count=*/1);
} else {
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOn",
/*expected_count=*/1);
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOff",
/*expected_count=*/1);
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.DiveIn",
/*expected_count=*/0);
histogram_tester->ExpectBucketCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOn",
QsFeatureCatalogName::kDarkMode,
/*expected_count=*/1);
}
system_tray->CloseBubble(); PressLabel();
if (IsQsRevampEnabled()) {
histogram_tester->ExpectTotalCount("Ash.QuickSettings.FeaturePod.ToggledOn",
/*expected_count=*/1);
histogram_tester->ExpectTotalCount(
"Ash.QuickSettings.FeaturePod.ToggledOff",
/*expected_count=*/1);
histogram_tester->ExpectTotalCount("Ash.QuickSettings.FeaturePod.DiveIn",
/*expected_count=*/0);
histogram_tester->ExpectBucketCount("Ash.QuickSettings.FeaturePod.DiveIn",
QsFeatureCatalogName::kDarkMode,
/*expected_count=*/0);
} else {
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOn",
/*expected_count=*/1);
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOff",
/*expected_count=*/1);
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.DiveIn",
/*expected_count=*/1);
histogram_tester->ExpectBucketCount(
"Ash.UnifiedSystemView.FeaturePod.DiveIn",
QsFeatureCatalogName::kDarkMode,
/*expected_count=*/1);
}
} }
} // namespace ash } // namespace ash

@@ -5,6 +5,7 @@
#include "ash/system/night_light/night_light_feature_pod_controller.h" #include "ash/system/night_light/night_light_feature_pod_controller.h"
#include <string> #include <string>
#include "ash/constants/ash_features.h"
#include "ash/constants/quick_settings_catalogs.h" #include "ash/constants/quick_settings_catalogs.h"
#include "ash/public/cpp/system_tray_client.h" #include "ash/public/cpp/system_tray_client.h"
#include "ash/resources/vector_icons/vector_icons.h" #include "ash/resources/vector_icons/vector_icons.h"
@@ -17,6 +18,7 @@
#include "ash/system/night_light/night_light_controller_impl.h" #include "ash/system/night_light/night_light_controller_impl.h"
#include "ash/system/tray/tray_popup_utils.h" #include "ash/system/tray/tray_popup_utils.h"
#include "ash/system/unified/feature_pod_button.h" #include "ash/system/unified/feature_pod_button.h"
#include "ash/system/unified/feature_tile.h"
#include "ash/system/unified/quick_settings_metrics_util.h" #include "ash/system/unified/quick_settings_metrics_util.h"
#include "ash/system/unified/unified_system_tray_controller.h" #include "ash/system/unified/unified_system_tray_controller.h"
#include "base/i18n/time_formatting.h" #include "base/i18n/time_formatting.h"
@@ -39,7 +41,6 @@ void LogUserNightLightEvent(const bool enabled) {
NightLightFeaturePodController::NightLightFeaturePodController( NightLightFeaturePodController::NightLightFeaturePodController(
UnifiedSystemTrayController* tray_controller) UnifiedSystemTrayController* tray_controller)
: tray_controller_(tray_controller) { : tray_controller_(tray_controller) {
DCHECK(tray_controller_);
Shell::Get()->system_tray_model()->clock()->AddObserver(this); Shell::Get()->system_tray_model()->clock()->AddObserver(this);
} }
@@ -65,6 +66,28 @@ FeaturePodButton* NightLightFeaturePodController::CreateButton() {
return button_; return button_;
} }
std::unique_ptr<FeatureTile> NightLightFeaturePodController::CreateTile(
bool compact) {
DCHECK(features::IsQsRevampEnabled());
DCHECK(!tile_);
auto tile = std::make_unique<FeatureTile>(
base::BindRepeating(&NightLightFeaturePodController::OnIconPressed,
weak_factory_.GetWeakPtr()),
/*is_togglable=*/true);
tile_ = tile.get();
const bool visible =
Shell::Get()->session_controller()->ShouldEnableSettings();
tile_->SetVisible(visible);
if (visible) {
TrackVisibilityUMA();
}
tile_->SetLabel(
l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_BUTTON_LABEL));
UpdateTile();
return tile;
}
QsFeatureCatalogName NightLightFeaturePodController::GetCatalogName() { QsFeatureCatalogName NightLightFeaturePodController::GetCatalogName() {
return QsFeatureCatalogName::kNightLight; return QsFeatureCatalogName::kNightLight;
} }
@@ -76,7 +99,7 @@ void NightLightFeaturePodController::OnIconPressed() {
Shell::Get()->night_light_controller()->Toggle(); Shell::Get()->night_light_controller()->Toggle();
LogUserNightLightEvent(Shell::Get()->night_light_controller()->GetEnabled()); LogUserNightLightEvent(Shell::Get()->night_light_controller()->GetEnabled());
UpdateButton(); Update();
if (Shell::Get()->night_light_controller()->GetEnabled()) { if (Shell::Get()->night_light_controller()->GetEnabled()) {
base::RecordAction( base::RecordAction(
@@ -88,6 +111,9 @@ void NightLightFeaturePodController::OnIconPressed() {
} }
void NightLightFeaturePodController::OnLabelPressed() { void NightLightFeaturePodController::OnLabelPressed() {
if (features::IsQsRevampEnabled()) {
return;
}
if (TrayPopupUtils::CanOpenWebUISettings()) { if (TrayPopupUtils::CanOpenWebUISettings()) {
TrackDiveInUMA(); TrackDiveInUMA();
base::RecordAction( base::RecordAction(
@@ -98,20 +124,20 @@ void NightLightFeaturePodController::OnLabelPressed() {
} }
void NightLightFeaturePodController::OnDateFormatChanged() { void NightLightFeaturePodController::OnDateFormatChanged() {
UpdateButton(); Update();
} }
void NightLightFeaturePodController::OnSystemClockTimeUpdated() { void NightLightFeaturePodController::OnSystemClockTimeUpdated() {
UpdateButton(); Update();
} }
void NightLightFeaturePodController::OnSystemClockCanSetTimeChanged( void NightLightFeaturePodController::OnSystemClockCanSetTimeChanged(
bool can_set_time) { bool can_set_time) {
UpdateButton(); Update();
} }
void NightLightFeaturePodController::Refresh() { void NightLightFeaturePodController::Refresh() {
UpdateButton(); Update();
} }
const std::u16string NightLightFeaturePodController::GetPodSubLabel() { const std::u16string NightLightFeaturePodController::GetPodSubLabel() {
@@ -148,6 +174,14 @@ const std::u16string NightLightFeaturePodController::GetPodSubLabel() {
} }
} }
void NightLightFeaturePodController::Update() {
if (features::IsQsRevampEnabled()) {
UpdateTile();
return;
}
UpdateButton();
}
void NightLightFeaturePodController::UpdateButton() { void NightLightFeaturePodController::UpdateButton() {
auto* controller = Shell::Get()->night_light_controller(); auto* controller = Shell::Get()->night_light_controller();
const bool is_enabled = controller->GetEnabled(); const bool is_enabled = controller->GetEnabled();
@@ -161,4 +195,19 @@ void NightLightFeaturePodController::UpdateButton() {
IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_TOGGLE_TOOLTIP, tooltip_state)); IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_TOGGLE_TOOLTIP, tooltip_state));
} }
void NightLightFeaturePodController::UpdateTile() {
auto* controller = Shell::Get()->night_light_controller();
const bool is_enabled = controller->GetEnabled();
tile_->SetToggled(is_enabled);
tile_->SetSubLabel(GetPodSubLabel());
std::u16string tooltip_state = l10n_util::GetStringUTF16(
is_enabled ? IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_ENABLED_STATE_TOOLTIP
: IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_DISABLED_STATE_TOOLTIP);
tile_->SetTooltipText(l10n_util::GetStringFUTF16(
IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_TOGGLE_TOOLTIP, tooltip_state));
tile_->SetVectorIcon(is_enabled ? kUnifiedMenuNightLightIcon
: kUnifiedMenuNightLightOffIcon);
}
} // namespace ash } // namespace ash

@@ -10,9 +10,11 @@
#include "ash/constants/quick_settings_catalogs.h" #include "ash/constants/quick_settings_catalogs.h"
#include "ash/system/model/clock_observer.h" #include "ash/system/model/clock_observer.h"
#include "ash/system/unified/feature_pod_controller_base.h" #include "ash/system/unified/feature_pod_controller_base.h"
#include "base/memory/weak_ptr.h"
namespace ash { namespace ash {
class FeatureTile;
class UnifiedSystemTrayController; class UnifiedSystemTrayController;
// Controller of a feature pod button that toggles night light mode. // Controller of a feature pod button that toggles night light mode.
@@ -32,6 +34,7 @@ class ASH_EXPORT NightLightFeaturePodController
// FeaturePodControllerBase: // FeaturePodControllerBase:
FeaturePodButton* CreateButton() override; FeaturePodButton* CreateButton() override;
std::unique_ptr<FeatureTile> CreateTile(bool compact = false) override;
QsFeatureCatalogName GetCatalogName() override; QsFeatureCatalogName GetCatalogName() override;
void OnIconPressed() override; void OnIconPressed() override;
void OnLabelPressed() override; void OnLabelPressed() override;
@@ -47,12 +50,23 @@ class ASH_EXPORT NightLightFeaturePodController
// current status and schedule type of night light. // current status and schedule type of night light.
const std::u16string GetPodSubLabel(); const std::u16string GetPodSubLabel();
// For QsRevamp: Updates `button_` or `tile_` based on whether QsRevamp flag
// is on.
void Update();
// Updates the toggle state, sub label, and icon tooltip of the `button_`. // Updates the toggle state, sub label, and icon tooltip of the `button_`.
void UpdateButton(); void UpdateButton();
UnifiedSystemTrayController* const tray_controller_; // For QsRevamp: Updates the toggle state, sub label, and icon tooltip of the
// `tile_`.
void UpdateTile();
UnifiedSystemTrayController* const tray_controller_;
// Owned by the views hierarchy.
FeaturePodButton* button_ = nullptr; FeaturePodButton* button_ = nullptr;
FeatureTile* tile_ = nullptr;
base::WeakPtrFactory<NightLightFeaturePodController> weak_factory_{this};
}; };
} // namespace ash } // namespace ash

@@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "ash/system/night_light/night_light_feature_pod_controller.h" #include "ash/system/night_light/night_light_feature_pod_controller.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/quick_settings_catalogs.h" #include "ash/constants/quick_settings_catalogs.h"
#include "ash/session/session_controller_impl.h" #include "ash/session/session_controller_impl.h"
#include "ash/shell.h" #include "ash/shell.h"
@@ -11,6 +12,7 @@
#include "ash/system/model/system_tray_model.h" #include "ash/system/model/system_tray_model.h"
#include "ash/system/night_light/night_light_controller_impl.h" #include "ash/system/night_light/night_light_controller_impl.h"
#include "ash/system/unified/feature_pod_button.h" #include "ash/system/unified/feature_pod_button.h"
#include "ash/system/unified/feature_tile.h"
#include "ash/system/unified/unified_system_tray.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_bubble.h"
#include "ash/test/ash_test_base.h" #include "ash/test/ash_test_base.h"
@@ -21,75 +23,120 @@
namespace ash { namespace ash {
class NightLightFeaturePodControllerTest : public AshTestBase { class NightLightFeaturePodControllerTest
: public AshTestBase,
public testing::WithParamInterface<bool> {
public: public:
NightLightFeaturePodControllerTest() {
if (IsQsRevampEnabled()) {
feature_list_.InitWithFeatures({features::kQsRevamp}, {});
} else {
feature_list_.InitWithFeatures({}, {features::kQsRevamp});
}
}
bool IsQsRevampEnabled() const { return GetParam(); }
void SetUp() override { void SetUp() override {
AshTestBase::SetUp(); AshTestBase::SetUp();
system_tray_ = GetPrimaryUnifiedSystemTray();
UnifiedSystemTray* system_tray = GetPrimaryUnifiedSystemTray(); system_tray_->ShowBubble();
system_tray->ShowBubble();
feature_pod_controller_ = std::make_unique<NightLightFeaturePodController>(
system_tray->bubble()->unified_system_tray_controller());
feature_pod_button_.reset(feature_pod_controller_->CreateButton());
} }
void TearDown() override { void TearDown() override {
feature_pod_controller_.reset(); button_.reset();
feature_pod_button_.reset(); tile_.reset();
controller_.reset();
system_tray_->CloseBubble();
AshTestBase::TearDown(); AshTestBase::TearDown();
} }
void CreateButton() {
controller_ = std::make_unique<NightLightFeaturePodController>(
system_tray_->bubble()->unified_system_tray_controller());
if (IsQsRevampEnabled()) {
tile_ = controller_->CreateTile();
} else {
button_ = base::WrapUnique(controller_->CreateButton());
}
}
bool IsButtonVisible() {
return IsQsRevampEnabled() ? tile_->GetVisible() : button_->GetVisible();
}
bool IsButtonToggled() {
return IsQsRevampEnabled() ? tile_->IsToggled() : button_->IsToggled();
}
protected: protected:
NightLightFeaturePodController* feature_pod_controller() { void PressIcon() { controller_->OnIconPressed(); }
return feature_pod_controller_.get();
void PressLabel() { controller_->OnLabelPressed(); }
const std::u16string& GetButtonLabelText() {
if (IsQsRevampEnabled()) {
return tile_->sub_label()->GetText();
}
return button_->label_button_->GetSubLabelText();
} }
FeaturePodButton* feature_pod_button() { return feature_pod_button_.get(); }
const ash::FeaturePodLabelButton* feature_pod_label_button() {
return feature_pod_button_->label_button_;
}
void PressIcon() { feature_pod_controller_->OnIconPressed(); }
void PressLabel() { feature_pod_controller_->OnLabelPressed(); }
private: private:
std::unique_ptr<FeaturePodButton> feature_pod_button_; base::test::ScopedFeatureList feature_list_;
std::unique_ptr<NightLightFeaturePodController> feature_pod_controller_; UnifiedSystemTray* system_tray_;
std::unique_ptr<NightLightFeaturePodController> controller_;
std::unique_ptr<FeaturePodButton> button_;
std::unique_ptr<FeatureTile> tile_;
}; };
INSTANTIATE_TEST_SUITE_P(QsRevamp,
NightLightFeaturePodControllerTest,
testing::Bool());
TEST_P(NightLightFeaturePodControllerTest, ButtonVisibility) {
// The button is visible in an active session.
CreateButton();
EXPECT_TRUE(IsButtonVisible());
// The button is not visible at the lock screen.
GetSessionControllerClient()->LockScreen();
CreateButton();
EXPECT_FALSE(IsButtonVisible());
}
// Tests that toggling night light from the system tray switches the color // Tests that toggling night light from the system tray switches the color
// mode and its button label properly. // mode and its button label properly.
TEST_F(NightLightFeaturePodControllerTest, Toggle) { TEST_P(NightLightFeaturePodControllerTest, Toggle) {
CreateButton();
NightLightControllerImpl* controller = Shell::Get()->night_light_controller(); NightLightControllerImpl* controller = Shell::Get()->night_light_controller();
// Check that the feature pod button and its label reflects the default // Check that the feature pod button and its label reflects the default
// Night light off without any auto scheduling. // Night light off without any auto scheduling.
EXPECT_FALSE(controller->GetEnabled()); EXPECT_FALSE(controller->GetEnabled());
EXPECT_FALSE(feature_pod_button()->IsToggled()); EXPECT_FALSE(IsButtonToggled());
EXPECT_EQ(NightLightController::ScheduleType::kNone, EXPECT_EQ(NightLightController::ScheduleType::kNone,
controller->GetScheduleType()); controller->GetScheduleType());
EXPECT_EQ( EXPECT_EQ(
l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_OFF_STATE), l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_OFF_STATE),
feature_pod_label_button()->GetSubLabelText()); GetButtonLabelText());
// Toggling the button should enable night light and update the button label // Toggling the button should enable night light and update the button label
// correctly and maintaining no scheduling. // correctly and maintaining no scheduling.
feature_pod_controller()->OnIconPressed(); PressIcon();
EXPECT_TRUE(controller->GetEnabled()); EXPECT_TRUE(controller->GetEnabled());
EXPECT_TRUE(feature_pod_button()->IsToggled()); EXPECT_TRUE(IsButtonToggled());
EXPECT_EQ(NightLightController::ScheduleType::kNone, EXPECT_EQ(NightLightController::ScheduleType::kNone,
controller->GetScheduleType()); controller->GetScheduleType());
EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_ON_STATE), EXPECT_EQ(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NIGHT_LIGHT_ON_STATE),
feature_pod_label_button()->GetSubLabelText()); GetButtonLabelText());
} }
// Tests that toggling sunset-to-sunrise-scheduled night light from the system // Tests that toggling sunset-to-sunrise-scheduled night light from the system
// tray while switches the color mode temporarily and maintains the auto // tray while switches the color mode temporarily and maintains the auto
// scheduling. // scheduling.
TEST_F(NightLightFeaturePodControllerTest, SunsetToSunrise) { TEST_P(NightLightFeaturePodControllerTest, SunsetToSunrise) {
CreateButton();
// Enable sunset-to-sunrise scheduling. // Enable sunset-to-sunrise scheduling.
NightLightControllerImpl* controller = Shell::Get()->night_light_controller(); NightLightControllerImpl* controller = Shell::Get()->night_light_controller();
controller->SetScheduleType( controller->SetScheduleType(
@@ -105,28 +152,28 @@ TEST_F(NightLightFeaturePodControllerTest, SunsetToSunrise) {
// Pressing the night light button should switch the status but keep // Pressing the night light button should switch the status but keep
// sunset-to-sunrise scheduling. // sunset-to-sunrise scheduling.
bool enabled = controller->GetEnabled(); bool enabled = controller->GetEnabled();
feature_pod_controller()->OnIconPressed(); PressIcon();
EXPECT_EQ(NightLightController::ScheduleType::kSunsetToSunrise, EXPECT_EQ(NightLightController::ScheduleType::kSunsetToSunrise,
controller->GetScheduleType()); controller->GetScheduleType());
EXPECT_EQ(!enabled, controller->GetEnabled()); EXPECT_EQ(!enabled, controller->GetEnabled());
EXPECT_EQ(!enabled, feature_pod_button()->IsToggled()); EXPECT_EQ(!enabled, IsButtonToggled());
EXPECT_EQ(!enabled ? sublabel_on : sublabel_off, EXPECT_EQ(!enabled ? sublabel_on : sublabel_off, GetButtonLabelText());
feature_pod_label_button()->GetSubLabelText());
// Pressing the night light button should switch the status but keep // Pressing the night light button should switch the status but keep
// sunset-to-sunrise scheduling. // sunset-to-sunrise scheduling.
feature_pod_controller()->OnIconPressed(); PressIcon();
EXPECT_EQ(NightLightController::ScheduleType::kSunsetToSunrise, EXPECT_EQ(NightLightController::ScheduleType::kSunsetToSunrise,
controller->GetScheduleType()); controller->GetScheduleType());
EXPECT_EQ(enabled, controller->GetEnabled()); EXPECT_EQ(enabled, controller->GetEnabled());
EXPECT_EQ(enabled, feature_pod_button()->IsToggled()); EXPECT_EQ(enabled, IsButtonToggled());
EXPECT_EQ(enabled ? sublabel_on : sublabel_off, EXPECT_EQ(enabled ? sublabel_on : sublabel_off, GetButtonLabelText());
feature_pod_label_button()->GetSubLabelText());
} }
// Tests that custom-scheduled night light displays the right custom start or // Tests that custom-scheduled night light displays the right custom start or
// end time for custom schedule type on the button label of the system tray. // end time for custom schedule type on the button label of the system tray.
TEST_F(NightLightFeaturePodControllerTest, Custom) { TEST_P(NightLightFeaturePodControllerTest, Custom) {
CreateButton();
// Enable custom scheduling. // Enable custom scheduling.
NightLightControllerImpl* controller = Shell::Get()->night_light_controller(); NightLightControllerImpl* controller = Shell::Get()->night_light_controller();
controller->SetScheduleType(NightLightController::ScheduleType::kCustom); controller->SetScheduleType(NightLightController::ScheduleType::kCustom);
@@ -151,98 +198,167 @@ TEST_F(NightLightFeaturePodControllerTest, Custom) {
// Pressing the night light button should switch the status and update the // Pressing the night light button should switch the status and update the
// label but keep the custom scheduling. // label but keep the custom scheduling.
bool enabled = controller->GetEnabled(); bool enabled = controller->GetEnabled();
feature_pod_controller()->OnIconPressed(); PressIcon();
EXPECT_EQ(NightLightController::ScheduleType::kCustom, EXPECT_EQ(NightLightController::ScheduleType::kCustom,
controller->GetScheduleType()); controller->GetScheduleType());
EXPECT_EQ(!enabled, controller->GetEnabled()); EXPECT_EQ(!enabled, controller->GetEnabled());
EXPECT_EQ(!enabled, feature_pod_button()->IsToggled()); EXPECT_EQ(!enabled, IsButtonToggled());
EXPECT_EQ(!enabled ? sublabel_on : sublabel_off, EXPECT_EQ(!enabled ? sublabel_on : sublabel_off, GetButtonLabelText());
feature_pod_label_button()->GetSubLabelText());
// Pressing the night light button should switch the status and update the // Pressing the night light button should switch the status and update the
// label but keep the custom scheduling. // label but keep the custom scheduling.
feature_pod_controller()->OnIconPressed(); PressIcon();
EXPECT_EQ(NightLightController::ScheduleType::kCustom, EXPECT_EQ(NightLightController::ScheduleType::kCustom,
controller->GetScheduleType()); controller->GetScheduleType());
EXPECT_EQ(enabled, controller->GetEnabled()); EXPECT_EQ(enabled, controller->GetEnabled());
EXPECT_EQ(enabled, feature_pod_button()->IsToggled()); EXPECT_EQ(enabled, IsButtonToggled());
EXPECT_EQ(enabled ? sublabel_on : sublabel_off, EXPECT_EQ(enabled ? sublabel_on : sublabel_off, GetButtonLabelText());
feature_pod_label_button()->GetSubLabelText());
} }
TEST_F(NightLightFeaturePodControllerTest, IconUMATracking) { TEST_P(NightLightFeaturePodControllerTest, IconUMATracking) {
CreateButton();
// Disable sunset-to-sunrise scheduling. // Disable sunset-to-sunrise scheduling.
NightLightControllerImpl* controller = Shell::Get()->night_light_controller(); NightLightControllerImpl* controller = Shell::Get()->night_light_controller();
controller->SetScheduleType(NightLightController::ScheduleType::kNone); controller->SetScheduleType(NightLightController::ScheduleType::kNone);
// No metrics logged before clicking on any views. // No metrics logged before clicking on any views.
auto histogram_tester = std::make_unique<base::HistogramTester>(); auto histogram_tester = std::make_unique<base::HistogramTester>();
histogram_tester->ExpectTotalCount( if (IsQsRevampEnabled()) {
"Ash.UnifiedSystemView.FeaturePod.ToggledOn", histogram_tester->ExpectTotalCount("Ash.QuickSettings.FeaturePod.ToggledOn",
/*count=*/0); /*expected_count=*/0);
histogram_tester->ExpectTotalCount( histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOff", "Ash.QuickSettings.FeaturePod.ToggledOff",
/*count=*/0); /*expected_count=*/0);
histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn", histogram_tester->ExpectTotalCount("Ash.QuickSettings.FeaturePod.DiveIn",
/*count=*/0); /*expected_count=*/0);
} else {
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOn",
/*expected_count=*/0);
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOff",
/*expected_count=*/0);
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.DiveIn",
/*expected_count=*/0);
}
// Toggle on the nightlight feature when pressing on the icon. // Toggle on the nightlight feature when pressing on the icon.
PressIcon(); PressIcon();
histogram_tester->ExpectTotalCount( if (IsQsRevampEnabled()) {
"Ash.UnifiedSystemView.FeaturePod.ToggledOn", histogram_tester->ExpectTotalCount("Ash.QuickSettings.FeaturePod.ToggledOn",
/*count=*/1); /*expected_count=*/1);
histogram_tester->ExpectTotalCount( histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOff", "Ash.QuickSettings.FeaturePod.ToggledOff",
/*count=*/0); /*expected_count=*/0);
histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn", histogram_tester->ExpectTotalCount("Ash.QuickSettings.FeaturePod.DiveIn",
/*count=*/0); /*expected_count=*/0);
histogram_tester->ExpectBucketCount( histogram_tester->ExpectBucketCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOn", "Ash.QuickSettings.FeaturePod.ToggledOn",
QsFeatureCatalogName::kNightLight, QsFeatureCatalogName::kNightLight,
/*expected_count=*/1); /*expected_count=*/1);
} else {
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOn",
/*expected_count=*/1);
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOff",
/*expected_count=*/0);
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.DiveIn",
/*expected_count=*/0);
histogram_tester->ExpectBucketCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOn",
QsFeatureCatalogName::kNightLight,
/*expected_count=*/1);
}
// Toggle off the nightlight feature when pressing on the icon again. // Toggle off the nightlight feature when pressing on the icon again.
PressIcon(); PressIcon();
histogram_tester->ExpectTotalCount( if (IsQsRevampEnabled()) {
"Ash.UnifiedSystemView.FeaturePod.ToggledOn", histogram_tester->ExpectTotalCount("Ash.QuickSettings.FeaturePod.ToggledOn",
/*count=*/1); /*expected_count=*/1);
histogram_tester->ExpectTotalCount( histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOff", "Ash.QuickSettings.FeaturePod.ToggledOff",
/*count=*/1); /*expected_count=*/1);
histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn", histogram_tester->ExpectTotalCount("Ash.QuickSettings.FeaturePod.DiveIn",
/*count=*/0); /*expected_count=*/0);
histogram_tester->ExpectBucketCount( histogram_tester->ExpectBucketCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOff", "Ash.QuickSettings.FeaturePod.ToggledOff",
QsFeatureCatalogName::kNightLight, QsFeatureCatalogName::kNightLight,
/*expected_count=*/1); /*expected_count=*/1);
} else {
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOn",
/*expected_count=*/1);
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOff",
/*expected_count=*/1);
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.DiveIn",
/*expected_count=*/0);
histogram_tester->ExpectBucketCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOff",
QsFeatureCatalogName::kNightLight,
/*expected_count=*/1);
}
} }
TEST_F(NightLightFeaturePodControllerTest, LabelUMATracking) { TEST_P(NightLightFeaturePodControllerTest, LabelUMATracking) {
CreateButton();
// No metrics logged before clicking on any views. // No metrics logged before clicking on any views.
auto histogram_tester = std::make_unique<base::HistogramTester>(); auto histogram_tester = std::make_unique<base::HistogramTester>();
histogram_tester->ExpectTotalCount( if (IsQsRevampEnabled()) {
"Ash.UnifiedSystemView.FeaturePod.ToggledOn", histogram_tester->ExpectTotalCount("Ash.QuickSettings.FeaturePod.ToggledOn",
/*count=*/0); /*expected_count=*/0);
histogram_tester->ExpectTotalCount( histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOff", "Ash.QuickSettings.FeaturePod.ToggledOff",
/*count=*/0); /*expected_count=*/0);
histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn", histogram_tester->ExpectTotalCount("Ash.QuickSettings.FeaturePod.DiveIn",
/*count=*/0); /*expected_count=*/0);
} else {
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOn",
/*expected_count=*/0);
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOff",
/*expected_count=*/0);
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.DiveIn",
/*expected_count=*/0);
}
// Show nightlight detailed view (settings window) when pressing on the // Show nightlight detailed view (settings window) when pressing on the
// label. // label.
PressLabel(); PressLabel();
histogram_tester->ExpectTotalCount( if (IsQsRevampEnabled()) {
"Ash.UnifiedSystemView.FeaturePod.ToggledOn", histogram_tester->ExpectTotalCount("Ash.QuickSettings.FeaturePod.ToggledOn",
/*count=*/0); /*expected_count=*/0);
histogram_tester->ExpectTotalCount( histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOff", "Ash.QuickSettings.FeaturePod.ToggledOff",
/*count=*/0); /*expected_count=*/0);
histogram_tester->ExpectTotalCount("Ash.UnifiedSystemView.FeaturePod.DiveIn", histogram_tester->ExpectTotalCount("Ash.QuickSettings.FeaturePod.DiveIn",
/*count=*/1); /*expected_count=*/0);
histogram_tester->ExpectBucketCount("Ash.UnifiedSystemView.FeaturePod.DiveIn", histogram_tester->ExpectBucketCount("Ash.QuickSettings.FeaturePod.DiveIn",
QsFeatureCatalogName::kNightLight, QsFeatureCatalogName::kNightLight,
/*expected_count=*/1); /*expected_count=*/0);
} else {
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOn",
/*expected_count=*/0);
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.ToggledOff",
/*expected_count=*/0);
histogram_tester->ExpectTotalCount(
"Ash.UnifiedSystemView.FeaturePod.DiveIn",
/*expected_count=*/1);
histogram_tester->ExpectBucketCount(
"Ash.UnifiedSystemView.FeaturePod.DiveIn",
QsFeatureCatalogName::kNightLight,
/*expected_count=*/1);
}
} }
} // namespace ash } // namespace ash

@@ -22,7 +22,7 @@
#include "ash/system/tray/tray_utils.h" #include "ash/system/tray/tray_utils.h"
#include "ash/system/unified/unified_system_tray.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_bubble.h"
#include "ash/system/unified/unified_system_tray_view.h" #include "base/functional/bind.h"
#include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_enums.mojom.h"
#include "ui/views/border.h" #include "ui/views/border.h"
@@ -189,6 +189,14 @@ void UnifiedSliderBubbleController::ShowBubble(SliderType slider_type) {
return; return;
} }
// When tray bubble is already shown, the brightness slider will get shown in
// display detailed view. Bail out if the display details are already showing
// to avoid resetting the bubble state.
if (slider_type == SLIDER_TYPE_DISPLAY_BRIGHTNESS && tray_->bubble() &&
tray_->bubble()->ShowingDisplayDetailedView()) {
return;
}
if (IsAnyMainBubbleShown()) { if (IsAnyMainBubbleShown()) {
tray_->EnsureBubbleExpanded(); tray_->EnsureBubbleExpanded();
@@ -279,7 +287,9 @@ void UnifiedSliderBubbleController::CreateSliderController() {
return; return;
case SLIDER_TYPE_DISPLAY_BRIGHTNESS: case SLIDER_TYPE_DISPLAY_BRIGHTNESS:
slider_controller_ = std::make_unique<UnifiedBrightnessSliderController>( slider_controller_ = std::make_unique<UnifiedBrightnessSliderController>(
tray_->model().get()); tray_->model().get(),
base::BindRepeating(&UnifiedSystemTray::ShowDisplayDetailedViewBubble,
base::Unretained(tray_)));
return; return;
case SLIDER_TYPE_KEYBOARD_BACKLIGHT_TOGGLE: case SLIDER_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
slider_controller_ = std::make_unique<KeyboardBacklightToggleController>( slider_controller_ = std::make_unique<KeyboardBacklightToggleController>(

@@ -379,11 +379,15 @@ void UnifiedSystemTray::ShowVolumeSliderBubble() {
} }
void UnifiedSystemTray::ShowAudioDetailedViewBubble() { void UnifiedSystemTray::ShowAudioDetailedViewBubble() {
// The settings menu bubble gains focus when |show_by_click| is true.
ShowBubble(); ShowBubble();
bubble_->ShowAudioDetailedView(); bubble_->ShowAudioDetailedView();
} }
void UnifiedSystemTray::ShowDisplayDetailedViewBubble() {
ShowBubble();
bubble_->ShowDisplayDetailedView();
}
void UnifiedSystemTray::ShowNetworkDetailedViewBubble() { void UnifiedSystemTray::ShowNetworkDetailedViewBubble() {
ShowBubble(); ShowBubble();
bubble_->ShowNetworkDetailedView(true /* force */); bubble_->ShowNetworkDetailedView(true /* force */);

@@ -136,6 +136,9 @@ class ASH_EXPORT UnifiedSystemTray
// Shows main bubble with audio settings detailed view. // Shows main bubble with audio settings detailed view.
void ShowAudioDetailedViewBubble(); void ShowAudioDetailedViewBubble();
// Shows main bubble with display settings detailed view.
void ShowDisplayDetailedViewBubble();
// Shows main bubble with network settings detailed view. // Shows main bubble with network settings detailed view.
void ShowNetworkDetailedViewBubble(); void ShowNetworkDetailedViewBubble();
@@ -247,6 +250,10 @@ class ASH_EXPORT UnifiedSystemTray
return channel_indicator_view_; return channel_indicator_view_;
} }
UnifiedSliderBubbleController* slider_bubble_controller() {
return slider_bubble_controller_.get();
}
private: private:
static const base::TimeDelta kNotificationCountUpdateDelay; static const base::TimeDelta kNotificationCountUpdateDelay;

@@ -192,6 +192,16 @@ void UnifiedSystemTrayBubble::ShowAudioDetailedView() {
controller_->ShowAudioDetailedView(); controller_->ShowAudioDetailedView();
} }
void UnifiedSystemTrayBubble::ShowDisplayDetailedView() {
if (!bubble_widget_) {
return;
}
DCHECK(unified_view_ || quick_settings_view_);
DCHECK(controller_);
controller_->ShowDisplayDetailedView();
}
void UnifiedSystemTrayBubble::ShowCalendarView( void UnifiedSystemTrayBubble::ShowCalendarView(
calendar_metrics::CalendarViewShowSource show_source, calendar_metrics::CalendarViewShowSource show_source,
calendar_metrics::CalendarEventSource event_source) { calendar_metrics::CalendarEventSource event_source) {
@@ -388,6 +398,10 @@ bool UnifiedSystemTrayBubble::ShowingAudioDetailedView() const {
return bubble_widget_ && controller_->showing_audio_detailed_view(); return bubble_widget_ && controller_->showing_audio_detailed_view();
} }
bool UnifiedSystemTrayBubble::ShowingDisplayDetailedView() const {
return bubble_widget_ && controller_->showing_display_detailed_view();
}
bool UnifiedSystemTrayBubble::ShowingCalendarView() const { bool UnifiedSystemTrayBubble::ShowingCalendarView() const {
return bubble_widget_ && controller_->showing_calendar_view(); return bubble_widget_ && controller_->showing_calendar_view();
} }

@@ -84,6 +84,9 @@ class ASH_EXPORT UnifiedSystemTrayBubble
// Show audio settings detailed view. // Show audio settings detailed view.
void ShowAudioDetailedView(); void ShowAudioDetailedView();
// Show display settings detailed view.
void ShowDisplayDetailedView();
// Show calendar view. // Show calendar view.
void ShowCalendarView(calendar_metrics::CalendarViewShowSource show_source, void ShowCalendarView(calendar_metrics::CalendarViewShowSource show_source,
calendar_metrics::CalendarEventSource event_source); calendar_metrics::CalendarEventSource event_source);
@@ -110,8 +113,10 @@ class ASH_EXPORT UnifiedSystemTrayBubble
// Fire a notification that an accessibility event has occured on this object. // Fire a notification that an accessibility event has occured on this object.
void NotifyAccessibilityEvent(ax::mojom::Event event, bool send_native_event); void NotifyAccessibilityEvent(ax::mojom::Event event, bool send_native_event);
// Whether the bubble is currently showing audio details or calendar view. // Whether the bubble is currently showing audio details or display details or
// calendar view.
bool ShowingAudioDetailedView() const; bool ShowingAudioDetailedView() const;
bool ShowingDisplayDetailedView() const;
bool ShowingCalendarView() const; bool ShowingCalendarView() const;
// TrayBubbleBase: // TrayBubbleBase:

@@ -22,6 +22,7 @@
#include "ash/system/audio/unified_volume_slider_controller.h" #include "ash/system/audio/unified_volume_slider_controller.h"
#include "ash/system/bluetooth/bluetooth_detailed_view_controller.h" #include "ash/system/bluetooth/bluetooth_detailed_view_controller.h"
#include "ash/system/bluetooth/bluetooth_feature_pod_controller.h" #include "ash/system/bluetooth/bluetooth_feature_pod_controller.h"
#include "ash/system/brightness/quick_settings_display_detailed_view_controller.h"
#include "ash/system/brightness/unified_brightness_slider_controller.h" #include "ash/system/brightness/unified_brightness_slider_controller.h"
#include "ash/system/camera/autozoom_feature_pod_controller.h" #include "ash/system/camera/autozoom_feature_pod_controller.h"
#include "ash/system/cast/cast_feature_pod_controller.h" #include "ash/system/cast/cast_feature_pod_controller.h"
@@ -179,7 +180,10 @@ UnifiedSystemTrayController::CreateUnifiedQuickSettingsView() {
unified_view->AddSliderView(volume_slider_controller_->CreateView()); unified_view->AddSliderView(volume_slider_controller_->CreateView());
brightness_slider_controller_ = brightness_slider_controller_ =
std::make_unique<UnifiedBrightnessSliderController>(model_); std::make_unique<UnifiedBrightnessSliderController>(
model_, views::Button::PressedCallback(base::BindRepeating(
&UnifiedSystemTrayController::ShowDisplayDetailedView,
base::Unretained(this))));
unified_view->AddSliderView(brightness_slider_controller_->CreateView()); unified_view->AddSliderView(brightness_slider_controller_->CreateView());
return unified_view; return unified_view;
@@ -205,7 +209,10 @@ UnifiedSystemTrayController::CreateQuickSettingsView(int max_height) {
qs_view->AddSliderView(unified_volume_view_); qs_view->AddSliderView(unified_volume_view_);
brightness_slider_controller_ = brightness_slider_controller_ =
std::make_unique<UnifiedBrightnessSliderController>(model_); std::make_unique<UnifiedBrightnessSliderController>(
model_, views::Button::PressedCallback(base::BindRepeating(
&UnifiedSystemTrayController::ShowDisplayDetailedView,
base::Unretained(this))));
unified_brightness_view_ = brightness_slider_controller_->CreateView(); unified_brightness_view_ = brightness_slider_controller_->CreateView();
qs_view->AddSliderView(unified_brightness_view_); qs_view->AddSliderView(unified_brightness_view_);
@@ -484,6 +491,12 @@ void UnifiedSystemTrayController::ShowAudioDetailedView() {
showing_audio_detailed_view_ = true; showing_audio_detailed_view_ = true;
} }
void UnifiedSystemTrayController::ShowDisplayDetailedView() {
ShowDetailedView(
std::make_unique<QuickSettingsDisplayDetailedViewController>(this));
showing_display_detailed_view_ = true;
}
void UnifiedSystemTrayController::ShowNotifierSettingsView() { void UnifiedSystemTrayController::ShowNotifierSettingsView() {
if (features::IsOsSettingsAppBadgingToggleEnabled()) { if (features::IsOsSettingsAppBadgingToggleEnabled()) {
return; return;
@@ -506,6 +519,7 @@ void UnifiedSystemTrayController::ShowCalendarView(
showing_calendar_view_ = true; showing_calendar_view_ = true;
showing_audio_detailed_view_ = false; showing_audio_detailed_view_ = false;
showing_display_detailed_view_ = false;
for (auto& observer : observers_) { for (auto& observer : observers_) {
observer.OnOpeningCalendarView(); observer.OnOpeningCalendarView();
@@ -526,6 +540,7 @@ void UnifiedSystemTrayController::TransitionToMainView(bool restore_focus) {
} }
showing_audio_detailed_view_ = false; showing_audio_detailed_view_ = false;
showing_display_detailed_view_ = false;
// Transfer `detailed_view_controller_` to a scoped object, which will be // Transfer `detailed_view_controller_` to a scoped object, which will be
// destroyed once it's out of this method's scope (after resetting // destroyed once it's out of this method's scope (after resetting
@@ -573,6 +588,7 @@ void UnifiedSystemTrayController::EnsureCollapsed() {
void UnifiedSystemTrayController::EnsureExpanded() { void UnifiedSystemTrayController::EnsureExpanded() {
if (detailed_view_controller_) { if (detailed_view_controller_) {
showing_audio_detailed_view_ = false; showing_audio_detailed_view_ = false;
showing_display_detailed_view_ = false;
if (features::IsQsRevampEnabled()) { if (features::IsQsRevampEnabled()) {
quick_settings_view_->ResetDetailedView(); quick_settings_view_->ResetDetailedView();
} else { } else {
@@ -761,6 +777,7 @@ void UnifiedSystemTrayController::ShowDetailedView(
} }
showing_audio_detailed_view_ = false; showing_audio_detailed_view_ = false;
showing_display_detailed_view_ = false;
if (features::IsQsRevampEnabled()) { if (features::IsQsRevampEnabled()) {
bubble_->UpdateBubbleHeight(/*is_showing_detiled_view=*/true); bubble_->UpdateBubbleHeight(/*is_showing_detiled_view=*/true);
quick_settings_view_->SetDetailedView(controller->CreateView()); quick_settings_view_->SetDetailedView(controller->CreateView());

@@ -127,6 +127,8 @@ class ASH_EXPORT UnifiedSystemTrayController
void ShowLocaleDetailedView(); void ShowLocaleDetailedView();
// Show the detailed view of audio. Called from the view. // Show the detailed view of audio. Called from the view.
void ShowAudioDetailedView(); void ShowAudioDetailedView();
// Show the detailed view of display. Called from the view.
void ShowDisplayDetailedView();
// Show the detailed view of notifier settings. Called from the view. // Show the detailed view of notifier settings. Called from the view.
void ShowNotifierSettingsView(); void ShowNotifierSettingsView();
// Show the detailed view of media controls. Called from the view. // Show the detailed view of media controls. Called from the view.
@@ -201,6 +203,10 @@ class ASH_EXPORT UnifiedSystemTrayController
return showing_audio_detailed_view_; return showing_audio_detailed_view_;
} }
bool showing_display_detailed_view() const {
return showing_display_detailed_view_;
}
bool showing_calendar_view() const { return showing_calendar_view_; } bool showing_calendar_view() const { return showing_calendar_view_; }
private: private:
@@ -320,6 +326,8 @@ class ASH_EXPORT UnifiedSystemTrayController
bool showing_audio_detailed_view_ = false; bool showing_audio_detailed_view_ = false;
bool showing_display_detailed_view_ = false;
bool showing_calendar_view_ = false; bool showing_calendar_view_ = false;
base::ObserverList<Observer> observers_; base::ObserverList<Observer> observers_;