0

vc-dlc-ui: Add warning view for kError DL state

Adding a warning label which is added for at most 2 effects.

The FeatureTile fetches DLC state on construction, which eventually
will propagate the state to Bubble view, where the warning labels
are updated.

Also added some pixel tests. go/vc-dlc-ui-pixel-things

Change-Id: I819bb40c81d00a8bafb3242ce3085e3936781abf
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5522002
Reviewed-by: Andre Le <leandre@chromium.org>
Auto-Submit: Alex Newcomer <newcomer@chromium.org>
Commit-Queue: Alex Newcomer <newcomer@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1312354}
This commit is contained in:
Alex Newcomer
2024-06-08 00:50:48 +00:00
committed by Chromium LUCI CQ
parent 169d071105
commit 5a7ed5d272
15 changed files with 419 additions and 46 deletions

@ -1794,6 +1794,12 @@ You can also use the keyboard shortcut. First, highlight text, then press <ph na
<message name="IDS_ASH_VIDEO_CONFERENCE_BUBBLE_LINUX_APP_WARNING_TEXT" desc="Text for the warning label placed inside the VC bubble when there's any linux app running.">
Effects are not supported on linux apps
</message>
<message name="IDS_ASH_VIDEO_CONFERENCE_BUBBLE_DLC_ERROR_ONE" desc="The text shown in a warning label when one DLC had an error downloading.">
<ph name="UNAVAILABLE_DLC">$1<ex>Live Caption</ex></ph> is not available.
</message>
<message name="IDS_ASH_VIDEO_CONFERENCE_BUBBLE_DLC_ERROR_TWO" desc="The text shown in a warning label when two DLCs had an error downloading.">
<ph name="UNAVAILABLE_DLC_ONE">$1<ex>Live Caption</ex></ph>, and <ph name="UNAVAILABLE_DLC_TWO">$2<ex>Background Blur</ex></ph> are not available.
</message>
<message name="IDS_ASH_VIDEO_CONFERENCE_TOAST_SPEAK_ON_MUTE_DETECTED" desc="A toast message that we show when user tries to speak while muted on system-level.">
Are you talking? Your mic is off. Select the mic to turn it on.
</message>

@ -0,0 +1 @@
e922b1bc0495ee19df0bc2ecddaa6e8258fb20d8

@ -0,0 +1 @@
b7d804bdb75169b7b48d067c47d52d871a3c68f4

@ -633,7 +633,6 @@ aggregate_vector_icons("ash_vector_icons") {
"video_conference_camera_capturing.icon",
"video_conference_camera_framing_on.icon",
"video_conference_camera_muted.icon",
"video_conference_linux_app_warning.icon",
"video_conference_live_caption_on.icon",
"video_conference_microphone_capturing.icon",
"video_conference_microphone_muted.icon",
@ -641,6 +640,7 @@ aggregate_vector_icons("ash_vector_icons") {
"video_conference_portrait_relight_on.icon",
"video_conference_screen_share.icon",
"video_conference_up_chevron.icon",
"video_conference_warning.icon",
"visibility.icon",
"visibility_off.icon",
"wallpaper.icon",

@ -5,6 +5,7 @@
#include "ash/system/video_conference/bubble/bubble_view.h"
#include <memory>
#include <string>
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/shell.h"
@ -19,6 +20,7 @@
#include "ash/system/video_conference/bubble/toggle_effects_view.h"
#include "ash/system/video_conference/effects/video_conference_tray_effects_manager.h"
#include "ash/system/video_conference/video_conference_tray_controller.h"
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "chromeos/crosapi/mojom/video_conference.mojom.h"
@ -43,8 +45,10 @@ namespace ash::video_conference {
namespace {
constexpr int kLinuxAppWarningViewTopPadding = 12;
constexpr int kLinuxAppWarningViewSpacing = 1;
constexpr int kLinuxAppWarningIconSize = 16;
constexpr int kDLCErrorWarningLabelTopPadding = 0;
constexpr int kWarningViewSpacing = 1;
constexpr int kWarningIconSize = 16;
constexpr int kScrollViewBetweenChildSpacing = 16;
CameraEffectsController* GetCameraEffectsController() {
@ -63,47 +67,40 @@ bool HasLinuxApps(const MediaApps& apps) {
return false;
}
// A view that will be display when there's Linux app(s) running along with
// other media apps, used to warn users that effects cannot be applied to Linux
// apps.
class LinuxAppWarningView : public views::View {
METADATA_HEADER(LinuxAppWarningView, views::View)
// Creates a view that will display a warning icon with text.
std::unique_ptr<views::View> CreateWarningView(
int warning_view_id,
int top_padding,
std::optional<int> warning_message = std::nullopt) {
auto view = std::make_unique<views::View>();
view->SetID(warning_view_id);
view->SetLayoutManager(std::make_unique<views::FlexLayout>())
->SetOrientation(views::LayoutOrientation::kHorizontal)
.SetMainAxisAlignment(views::LayoutAlignment::kCenter)
.SetCrossAxisAlignment(views::LayoutAlignment::kStretch)
.SetInteriorMargin(gfx::Insets::TLBR(top_padding, 0, 0, 0))
.SetDefault(
views::kMarginsKey,
gfx::Insets::TLBR(0, kWarningViewSpacing, 0, kWarningViewSpacing));
public:
LinuxAppWarningView() {
SetID(BubbleViewID::kLinuxAppWarningView);
SetLayoutManager(std::make_unique<views::FlexLayout>())
->SetOrientation(views::LayoutOrientation::kHorizontal)
.SetMainAxisAlignment(views::LayoutAlignment::kCenter)
.SetCrossAxisAlignment(views::LayoutAlignment::kStretch)
.SetInteriorMargin(
gfx::Insets::TLBR(kLinuxAppWarningViewTopPadding, 0, 0, 0))
.SetDefault(views::kMarginsKey,
gfx::Insets::TLBR(0, kLinuxAppWarningViewSpacing, 0,
kLinuxAppWarningViewSpacing));
auto icon = std::make_unique<views::ImageView>();
icon->SetImage(ui::ImageModel::FromVectorIcon(
kVideoConferenceWarningIcon, cros_tokens::kCrosSysOnSurfaceVariant,
kWarningIconSize));
view->AddChildView(std::move(icon));
auto icon = std::make_unique<views::ImageView>();
icon->SetImage(ui::ImageModel::FromVectorIcon(
kVideoConferenceLinuxAppWarningIcon,
cros_tokens::kCrosSysOnSurfaceVariant, kLinuxAppWarningIconSize));
AddChildView(std::move(icon));
auto label = std::make_unique<views::Label>();
label->SetText(l10n_util::GetStringUTF16(
IDS_ASH_VIDEO_CONFERENCE_BUBBLE_LINUX_APP_WARNING_TEXT));
TypographyProvider::Get()->StyleLabel(TypographyToken::kCrosAnnotation2,
*label);
AddChildView(std::move(label));
auto label = std::make_unique<views::Label>();
// Set a view ID so the label can be modified if necessary.
label->SetID(BubbleViewID::kWarningViewLabel);
if (warning_message.has_value()) {
label->SetText(l10n_util::GetStringUTF16(*warning_message));
}
TypographyProvider::Get()->StyleLabel(TypographyToken::kCrosAnnotation2,
*label);
view->AddChildView(std::move(label));
LinuxAppWarningView(const LinuxAppWarningView&) = delete;
LinuxAppWarningView& operator=(const LinuxAppWarningView&) = delete;
~LinuxAppWarningView() override = default;
};
BEGIN_METADATA(LinuxAppWarningView);
END_METADATA
return view;
}
} // namespace
@ -137,7 +134,10 @@ void BubbleView::AddedToWidget() {
if (HasLinuxApps(*media_apps_) &&
(has_toggle_effects || has_set_value_effects)) {
AddChildView(std::make_unique<LinuxAppWarningView>());
AddChildView(CreateWarningView(
BubbleViewID::kLinuxAppWarningView,
/*top_padding=*/kLinuxAppWarningViewTopPadding,
IDS_ASH_VIDEO_CONFERENCE_BUBBLE_LINUX_APP_WARNING_TEXT));
}
// Create the `views::ScrollView` to house the effects sections. This has to
@ -170,6 +170,16 @@ void BubbleView::AddedToWidget() {
if (has_toggle_effects) {
scroll_contents_view->AddChildView(
std::make_unique<ToggleEffectsView>(controller_));
auto error_warning_label_container_view =
CreateWarningView(BubbleViewID::kDLCDownloadsInErrorView,
/*top_padding=*/kDLCErrorWarningLabelTopPadding);
// Visibility will for most cases be false, if a DLC has an error in
// downloading, state updates will be fetched by any toggle effect's
// `FeatureTile`, then pushed from the controller via
// `OnDLCDownloadStateInError()`.
error_warning_label_container_view->SetVisible(false);
scroll_contents_view->AddChildView(
std::move(error_warning_label_container_view));
}
if (has_set_value_effects) {
scroll_contents_view->AddChildView(
@ -201,6 +211,66 @@ void BubbleView::SetBackgroundReplaceUiVisible(bool visible) {
ChildPreferredSizeChanged(set_camera_background_view_);
}
void BubbleView::OnDLCDownloadStateInError(
bool add_warning_view,
const std::u16string& feature_tile_title_string) {
auto* dlc_error_container_view =
GetViewByID(BubbleViewID::kDLCDownloadsInErrorView);
if (!dlc_error_container_view) {
return;
}
auto* dlc_error_label = static_cast<views::Label*>(
dlc_error_container_view->GetViewByID(BubbleViewID::kWarningViewLabel));
if (!dlc_error_label) {
return;
}
if (add_warning_view) {
if (std::size(feature_tile_error_string_ids_) == 2) {
return;
}
feature_tile_error_string_ids_.emplace(feature_tile_title_string);
} else {
auto it = std::find(feature_tile_error_string_ids_.begin(),
feature_tile_error_string_ids_.end(),
feature_tile_title_string);
if (it == feature_tile_error_string_ids_.end()) {
return;
}
feature_tile_error_string_ids_.erase(it);
}
// If the one and only string was removed, hide the view and reset it.
// Otherwise update the string.
if (feature_tile_error_string_ids_.empty()) {
dlc_error_container_view->SetVisible(false);
dlc_error_label->SetText(std::u16string());
return;
}
if (feature_tile_error_string_ids_.size() == 1) {
dlc_error_label->SetText(l10n_util::GetStringFUTF16(
IDS_ASH_VIDEO_CONFERENCE_BUBBLE_DLC_ERROR_ONE,
*feature_tile_error_string_ids_.begin()));
dlc_error_container_view->SetVisible(true);
return;
}
// Only two are supported, adding more would require more custom handling for
// the string below.
if (feature_tile_error_string_ids_.size() > 2u) {
return;
}
std::vector<std::u16string> string_ids(feature_tile_error_string_ids_.size());
std::copy(feature_tile_error_string_ids_.begin(),
feature_tile_error_string_ids_.end(), string_ids.begin());
dlc_error_label->SetText(
l10n_util::GetStringFUTF16(IDS_ASH_VIDEO_CONFERENCE_BUBBLE_DLC_ERROR_TWO,
string_ids, /*offsets=*/nullptr));
dlc_error_container_view->SetVisible(true);
}
BEGIN_METADATA(BubbleView)
END_METADATA

@ -37,6 +37,12 @@ class ASH_EXPORT BubbleView : public TrayBubbleView {
BubbleView& operator=(const BubbleView&) = delete;
~BubbleView() override;
// Called when DLC download state updates, used to add and update a warning
// string.
void OnDLCDownloadStateInError(
bool add_warning_view,
const std::u16string& feature_tile_title_string);
// views::View:
void AddedToWidget() override;
void ChildPreferredSizeChanged(View* child) override;
@ -50,6 +56,9 @@ class ASH_EXPORT BubbleView : public TrayBubbleView {
// Unowned by `BubbleView`.
raw_ptr<VideoConferenceTrayController> controller_;
// String id's of all `FeatureTile`'s that have an error downloading.
std::set<std::u16string> feature_tile_error_string_ids_;
const raw_ref<const MediaApps> media_apps_;
raw_ptr<views::View> set_camera_background_view_ = nullptr;

@ -41,10 +41,19 @@ enum BubbleViewID {
// Label which is a child of an individual "toggle" VC effect.
kToggleEffectLabel,
// The label with a warning icon, indicating that effects are not available
// The view with a warning icon and label, indicating that effects are not
// available
// for linux apps.
kLinuxAppWarningView,
// The view with a warning icon and a custom label for each DLC which had an
// error installing.
kDLCDownloadsInErrorView,
// The label in the generic warning view which is either
// `kLinuxAppWarningView` or `kDLCDownloadsInErrorView`.
kWarningViewLabel,
// The VC bubble that will be displayed when all the running media apps are
// Linux apps.
kLinuxAppBubbleView,

@ -131,6 +131,11 @@ class BubbleViewPixelTest
bool IsVcDlcUiEnabled() { return GetParam(); }
void ModifyDlcDownloadState(bool add_warning, std::u16string warning_label) {
static_cast<video_conference::BubbleView*>(bubble_view())
->OnDLCDownloadStateInError(add_warning, warning_label);
}
VideoConferenceTray* video_conference_tray() {
return StatusAreaWidgetTestHelper::GetStatusAreaWidget()
->video_conference_tray();
@ -413,4 +418,78 @@ TEST_P(BubbleViewPixelTest, ThreeToggleEffects) {
/*revision_number=*/6, GetToggleEffectsView()));
}
TEST_P(BubbleViewPixelTest, DLCUIInErrorShowsWarningLabelSingleError) {
if (!IsVcDlcUiEnabled()) {
return;
}
// Create a toggle effect so the warning label is available.
controller()->GetEffectsManager().RegisterDelegate(office_bunny());
// Click to open the bubble, toggle effect button should be visible.
LeftClickOn(video_conference_tray()->GetToggleBubbleButtonForTest());
ASSERT_TRUE(bubble_view());
ASSERT_TRUE(GetToggleEffectsView()->GetVisible());
// Add an error, make sure the label shows up.
ModifyDlcDownloadState(/*add_warning=*/true, u"test-feature-name1");
EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
"one-toggle-effects-view",
/*revision_number=*/1, bubble_view()));
// Add one set-value effect.
controller()->GetEffectsManager().RegisterDelegate(shaggy_fur());
// Hide and re-show the bubble so the set-value effect show sup.
LeftClickOn(video_conference_tray()->GetToggleBubbleButtonForTest());
ASSERT_FALSE(bubble_view());
LeftClickOn(video_conference_tray()->GetToggleBubbleButtonForTest());
ASSERT_TRUE(bubble_view());
ModifyDlcDownloadState(/*add_warning=*/true, u"test-feature-name1");
EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
"additional-set-value-view",
/*revision_number=*/1, bubble_view()));
}
TEST_P(BubbleViewPixelTest, DLCUIInErrorShowsWarningLabelMaxErrors) {
if (!IsVcDlcUiEnabled()) {
return;
}
// Create a toggle effect so the warning label is available.
controller()->GetEffectsManager().RegisterDelegate(office_bunny());
// Click to open the bubble, toggle effect button should be visible.
LeftClickOn(video_conference_tray()->GetToggleBubbleButtonForTest());
ASSERT_TRUE(bubble_view());
ASSERT_TRUE(GetToggleEffectsView()->GetVisible());
// Add 2 errors (the max), make sure the label shows up.
ModifyDlcDownloadState(/*add_warning=*/true, u"test-feature-name1");
ModifyDlcDownloadState(/*add_warning=*/true, u"test-feature-name2");
EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
"one-toggle-effects-view",
/*revision_number=*/1, bubble_view()));
// Add one set-value effect.
controller()->GetEffectsManager().RegisterDelegate(shaggy_fur());
// Hide and re-show the bubble so the set-value effect show sup.
LeftClickOn(video_conference_tray()->GetToggleBubbleButtonForTest());
ASSERT_FALSE(bubble_view());
LeftClickOn(video_conference_tray()->GetToggleBubbleButtonForTest());
ASSERT_TRUE(bubble_view());
ModifyDlcDownloadState(/*add_warning=*/true, u"test-feature-name1");
ModifyDlcDownloadState(/*add_warning=*/true, u"test-feature-name2");
EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
"additional-set-value-view",
/*revision_number=*/1, bubble_view()));
}
} // namespace ash::video_conference

@ -4,6 +4,8 @@
#include "ash/system/video_conference/bubble/bubble_view.h"
#include <string_view>
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_switches.h"
#include "ash/resources/vector_icons/vector_icons.h"
@ -129,7 +131,8 @@ class BubbleViewTest : public AshTestBase {
// AshTestBase:
void SetUp() override {
scoped_feature_list_.InitAndEnableFeature(
scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
scoped_feature_list_->InitAndEnableFeature(
features::kFeatureManagementVideoConference);
// Instantiates a fake controller (the real one is created in
@ -164,6 +167,7 @@ class BubbleViewTest : public AshTestBase {
shaggy_fur_.reset();
super_cuteness_.reset();
controller_.reset();
scoped_feature_list_.reset();
}
VideoConferenceTray* video_conference_tray() {
@ -224,7 +228,7 @@ class BubbleViewTest : public AshTestBase {
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
std::unique_ptr<FakeVideoConferenceTrayController> controller_;
std::unique_ptr<ash::fake_video_conference::OfficeBunnyEffect> office_bunny_;
std::unique_ptr<ash::fake_video_conference::ShaggyFurEffect> shaggy_fur_;
@ -455,6 +459,155 @@ TEST_F(BubbleViewTest, LinuxAppWarningView) {
EXPECT_TRUE(linux_app_warning_view()->GetVisible());
}
class DLCBubbleViewTest : public BubbleViewTest {
public:
DLCBubbleViewTest() = default;
DLCBubbleViewTest(const DLCBubbleViewTest&) = delete;
DLCBubbleViewTest& operator=(const DLCBubbleViewTest&) = delete;
~DLCBubbleViewTest() override = default;
// AshTestBase:
void SetUp() override {
scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
scoped_feature_list_->InitAndEnableFeature(features::kVcDlcUi);
BubbleViewTest::SetUp();
}
void TearDown() override {
// Unregister toggle effects that were registered to ensure the
// `VcTileUiControllers` are reset like they are in the production version.
controller()->GetEffectsManager().UnregisterDelegate(office_bunny());
BubbleViewTest::TearDown();
scoped_feature_list_.reset();
}
private:
std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
};
// Tests that an initialized BubbleView with no errors shows no warning label.
TEST_F(DLCBubbleViewTest, NoError) {
LeftClickOn(toggle_bubble_button());
ASSERT_TRUE(bubble_view());
ASSERT_TRUE(bubble_view()->GetVisible());
// The error view is not added with no toggle effects.
EXPECT_FALSE(bubble_view()->GetViewByID(kDLCDownloadsInErrorView));
LeftClickOn(toggle_bubble_button());
// Add one toggle effect and reshow, the error view should be present but not
// visible.
controller()->GetEffectsManager().RegisterDelegate(office_bunny());
LeftClickOn(toggle_bubble_button());
auto* error_view = bubble_view()->GetViewByID(kDLCDownloadsInErrorView);
EXPECT_TRUE(error_view);
EXPECT_FALSE(error_view->GetVisible());
}
// Tests adding and removing one error.
TEST_F(DLCBubbleViewTest, OneError) {
controller()->GetEffectsManager().RegisterDelegate(office_bunny());
LeftClickOn(toggle_bubble_button());
BubbleView* bubble_view_ptr = static_cast<BubbleView*>(bubble_view());
// Pass one error to the bubble view, it should get a warning label.
std::u16string test_feature_name = u"test_feature_name";
bubble_view_ptr->OnDLCDownloadStateInError(/*add_warning_view=*/true,
test_feature_name);
auto* error_container_view =
bubble_view()->GetViewByID(kDLCDownloadsInErrorView);
auto* dlc_error_label = static_cast<views::Label*>(
error_container_view->GetViewByID(BubbleViewID::kWarningViewLabel));
EXPECT_TRUE(error_container_view);
EXPECT_TRUE(dlc_error_label);
const std::u16string label_contents = dlc_error_label->GetText();
// Try to add a second error for `test_feature_name`, there should be no
// change.
bubble_view_ptr->OnDLCDownloadStateInError(/*add_warning_view=*/true,
test_feature_name);
EXPECT_EQ(label_contents, dlc_error_label->GetText());
// Remove the error, there should be a change.
bubble_view_ptr->OnDLCDownloadStateInError(/*add_warning_view=*/false,
test_feature_name);
EXPECT_EQ(std::u16string(), dlc_error_label->GetText());
EXPECT_FALSE(error_container_view->GetVisible());
}
// Tests adding/removing two+ errors.
TEST_F(DLCBubbleViewTest, TwoPlusErrors) {
controller()->GetEffectsManager().RegisterDelegate(office_bunny());
LeftClickOn(toggle_bubble_button());
BubbleView* bubble_view_ptr = static_cast<BubbleView*>(bubble_view());
// Pass one error to the bubble view, it should get a warning label.
std::u16string test_feature_name_1 = u"test_feature_name_1";
bubble_view_ptr->OnDLCDownloadStateInError(/*add_warning_view=*/true,
test_feature_name_1);
auto* error_container_view =
bubble_view()->GetViewByID(kDLCDownloadsInErrorView);
auto* dlc_error_label = static_cast<views::Label*>(
error_container_view->GetViewByID(BubbleViewID::kWarningViewLabel));
EXPECT_TRUE(error_container_view);
EXPECT_TRUE(dlc_error_label);
const std::u16string one_error_label = dlc_error_label->GetText();
// Add a second error for `test_feature_name_2`.
std::u16string test_feature_name_2 = u"test_feature_name_2";
bubble_view_ptr->OnDLCDownloadStateInError(/*add_warning_view=*/true,
test_feature_name_2);
const std::u16string two_error_label = dlc_error_label->GetText();
EXPECT_NE(one_error_label, two_error_label);
// Add a third error, there should be no effect as only two are supported.
bubble_view_ptr->OnDLCDownloadStateInError(/*add_warning_view=*/true,
u"feature_name_3");
EXPECT_EQ(dlc_error_label->GetText(), two_error_label);
// Remove the error, the label should return to the first one.
bubble_view_ptr->OnDLCDownloadStateInError(/*add_warning_view=*/false,
test_feature_name_2);
EXPECT_EQ(one_error_label, dlc_error_label->GetText());
// Remove the first error, the label should be empty.
bubble_view_ptr->OnDLCDownloadStateInError(/*add_warning_view=*/false,
test_feature_name_1);
EXPECT_EQ(std::u16string(), dlc_error_label->GetText());
EXPECT_FALSE(dlc_error_label->GetVisible());
}
// Tests removing errors that are not there does not crash.
TEST_F(DLCBubbleViewTest, ErrorsRemoved) {
controller()->GetEffectsManager().RegisterDelegate(office_bunny());
LeftClickOn(toggle_bubble_button());
// Try to remove errors that don't exist.
BubbleView* bubble_view_ptr = static_cast<BubbleView*>(bubble_view());
bubble_view_ptr->OnDLCDownloadStateInError(/*add_warning_view=*/false,
u"one");
bubble_view_ptr->OnDLCDownloadStateInError(/*add_warning_view=*/false,
u"two");
bubble_view_ptr->OnDLCDownloadStateInError(/*add_warning_view=*/false,
u"three");
auto* error_container_view =
bubble_view()->GetViewByID(kDLCDownloadsInErrorView);
auto* dlc_error_label = static_cast<views::Label*>(
error_container_view->GetViewByID(BubbleViewID::kWarningViewLabel));
EXPECT_FALSE(error_container_view->GetVisible());
EXPECT_EQ(dlc_error_label->GetText(), std::u16string());
}
// The four `bool` params are as follows, if 'true':
// 0 - The test effects depend on the camera being enabled.
// 1 - The test effects depend on the microphone being enabled.

@ -30,11 +30,21 @@ VcTileUiController::VcTileUiController(const VcHostedEffect* effect)
: effect_(effect) {
effect_id_ = effect->id();
effect_state_ = effect->GetState(/*index=*/0);
DlcserviceClient::Get()->AddObserver(this);
auto* dlc_service_client = DlcserviceClient::Get();
if (!dlc_service_client) {
// `dlc_service_client` may not exist in tests.
return;
}
dlc_service_client->AddObserver(this);
}
VcTileUiController::~VcTileUiController() {
DlcserviceClient::Get()->RemoveObserver(this);
auto* dlc_service_client = DlcserviceClient::Get();
if (!dlc_service_client) {
// `dlc_service_client` may not exist in tests.
return;
}
dlc_service_client->RemoveObserver(this);
}
std::unique_ptr<FeatureTile> VcTileUiController::CreateTile() {
@ -212,6 +222,10 @@ void VcTileUiController::OnDlcDownloadStateFetched(
return;
}
VideoConferenceTrayController::Get()->OnDlcDownloadStateFetched(
/*add_warning=*/download_state == FeatureTile::DownloadState::kError,
effect_state_->label_text());
tile_->SetDownloadState(download_state, progress);
}

@ -412,6 +412,17 @@ void VideoConferenceTray::OnScreenSharingStateChange(bool is_capturing_screen) {
}
}
void VideoConferenceTray::OnDlcDownloadStateChanged(
bool add_warning,
const std::u16string& feature_tile_title) {
auto* bubble_view = GetBubbleView();
if (!bubble_view) {
return;
}
views::AsViewClass<video_conference::BubbleView>(bubble_view)
->OnDLCDownloadStateInError(add_warning, feature_tile_title);
}
void VideoConferenceTray::OnCameraCapturingStateChange(bool is_capturing) {
camera_icon_->SetIsCapturing(is_capturing);
}

@ -153,6 +153,9 @@ class ASH_EXPORT VideoConferenceTray
void OnCameraCapturingStateChange(bool is_capturing) override;
void OnMicrophoneCapturingStateChange(bool is_capturing) override;
void OnScreenSharingStateChange(bool is_capturing_screen) override;
void OnDlcDownloadStateChanged(
bool add_warning,
const std::u16string& feature_tile_title) override;
// VideoConferenceTrayEffectsManager::Observer:
void OnEffectSupportStateChanged(VcEffectId effect_id,

@ -370,6 +370,14 @@ void VideoConferenceTrayController::OnSpeakOnMuteNudgeOptInAction(bool opt_in) {
ToastManager::Get()->Show(std::move(toast_data));
}
void VideoConferenceTrayController::OnDlcDownloadStateFetched(
bool add_warning,
const std::u16string& feature_tile_title) {
for (auto& observer : observer_list_) {
observer.OnDlcDownloadStateChanged(add_warning, feature_tile_title);
}
}
void VideoConferenceTrayController::CloseAllVcNudges() {
for (size_t i = 0; i < std::size(kNudgeIds); ++i) {
AnchoredNudgeManager::Get()->Cancel(kNudgeIds[i]);

@ -63,6 +63,12 @@ class ASH_EXPORT VideoConferenceTrayController
// Called when the state of screen sharing is changed.
virtual void OnScreenSharingStateChange(bool is_capturing_screen) = 0;
// Called when the Dlc download state is changed for `feature_tile_title` if
// any DLC was registered for that effect.
virtual void OnDlcDownloadStateChanged(
bool error,
const std::u16string& feature_tile_title) = 0;
};
VideoConferenceTrayController();
@ -122,6 +128,9 @@ class ASH_EXPORT VideoConferenceTrayController
// speak-on-mute feature. An `opt_in` value of false means the user opted out.
void OnSpeakOnMuteNudgeOptInAction(bool opt_in);
void OnDlcDownloadStateFetched(bool add_warning,
const std::u16string& feature_tile_title);
// Closes all nudges that are shown anchored to the VC tray, if any.
void CloseAllVcNudges();