0

VC UI: Add return to app panel UI

Given the information from VideoConferenceTrayController, renders a
ReturnToAppPanel that displays the information of currently running
media apps. This CL:
- Handles the data flow from the controller to the panel UI
- Defines all children classes and layouts of the panel, which shows all
the apps information and the aggregated summary at the top.

Screenshot:
- https://screenshot.googleplex.com/mi95mpsE7mx7Vn2
- https://screenshot.googleplex.com/8ZiB9bsd4Zkt3DA

Bug: b:253272945

Change-Id: Idf40f2f3b41b2e1e761a2db375758dc7a5bc5292
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4061999
Reviewed-by: Alex Newcomer <newcomer@chromium.org>
Reviewed-by: Ahmed Fakhry <afakhry@chromium.org>
Commit-Queue: Andre Le <leandre@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1087154}
This commit is contained in:
Andre Le
2022-12-27 19:33:20 +00:00
committed by Chromium LUCI CQ
parent 303fd02c2e
commit 2dd64a9997
14 changed files with 478 additions and 88 deletions

@ -1953,8 +1953,8 @@ component("ash") {
"system/video_conference/bubble/bubble_view.cc",
"system/video_conference/bubble/bubble_view.h",
"system/video_conference/bubble/bubble_view_ids.h",
"system/video_conference/bubble/return_to_app_button.cc",
"system/video_conference/bubble/return_to_app_button.h",
"system/video_conference/bubble/return_to_app_panel.cc",
"system/video_conference/bubble/return_to_app_panel.h",
"system/video_conference/bubble/set_value_effects_view.cc",
"system/video_conference/bubble/set_value_effects_view.h",
"system/video_conference/bubble/toggle_effects_view.cc",
@ -3217,6 +3217,7 @@ test("ash_unittests") {
"system/update/update_notification_controller_unittest.cc",
"system/usb_peripheral/usb_peripheral_notification_controller_unittest.cc",
"system/video_conference/bubble/bubble_view_unittest.cc",
"system/video_conference/bubble/return_to_app_panel_unittest.cc",
"system/video_conference/effects/video_conference_tray_effects_manager_unittest.cc",
"system/video_conference/video_conference_tray_unittest.cc",
"system/virtual_keyboard/virtual_keyboard_tray_unittest.cc",

@ -1448,6 +1448,9 @@ Style notes:
<message name="IDS_ASH_VIDEO_CONFERENCE_TOGGLE_BUBBLE_BUTTON_TOOLTIP" desc="The tooltip for the toggle bubble button in the video conference tray.">
Camera and audio controls
</message>
<message name="IDS_ASH_VIDEO_CONFERENCE_RETURN_TO_APP_SUMMARY_TEXT" desc="The summary text in the return to app panel of the video conference panel, specifying how many video conferencing apps are in used.">
Used by <ph name="APP_COUNT">$1<ex>2</ex></ph> apps
</message>
<!-- Phone Hub tray-->
<message name="IDS_ASH_PHONE_HUB_TRAY_ACCESSIBLE_NAME" desc="The accessible name of the Phone Hub tray bubble for screen readers.">

@ -0,0 +1 @@
9c5553d20f01fb19fb379a165d5a9f1368505370

@ -6,7 +6,7 @@
#include "ash/system/tray/tray_bubble_view.h"
#include "ash/system/video_conference/bubble/bubble_view_ids.h"
#include "ash/system/video_conference/bubble/return_to_app_button.h"
#include "ash/system/video_conference/bubble/return_to_app_panel.h"
#include "ash/system/video_conference/bubble/set_value_effects_view.h"
#include "ash/system/video_conference/bubble/toggle_effects_view.h"
#include "ash/system/video_conference/effects/video_conference_tray_effects_manager.h"
@ -37,10 +37,10 @@ BubbleView::BubbleView(const InitParams& init_params,
layout->SetMainAxisAlignment(views::LayoutAlignment::kCenter);
layout->SetCrossAxisAlignment(views::LayoutAlignment::kStretch);
// `ReturnToAppButton` resides in the top-level layout and isn't part of the
// `ReturnToAppPanel` resides in the top-level layout and isn't part of the
// scrollable area (that can't be added until the `BubbleView` officially has
// a parent waidget).
AddChildView(std::make_unique<ReturnToAppButton>());
AddChildView(std::make_unique<ReturnToAppPanel>());
}
void BubbleView::AddedToWidget() {

@ -1,56 +0,0 @@
// Copyright 2022 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/video_conference/bubble/return_to_app_button.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/style/ash_color_id.h"
#include "ash/system/video_conference/bubble/bubble_view_ids.h"
#include "ui/base/models/image_model.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/flex_layout.h"
namespace ash::video_conference {
namespace {
const int kBorderInsetDimension = 10;
const int kBackgroundRoundedRectRadius = 10;
} // namespace
// TODO(b/253274599, b/253274147, b/253272945) Implement actual "return to app"
// button functionality. This is a temporary placeholder for facilitating VC
// bubble development.
ReturnToAppButton::ReturnToAppButton() {
SetID(BubbleViewID::kReturnToApp);
views::FlexLayout* layout =
SetLayoutManager(std::make_unique<views::FlexLayout>());
layout->SetOrientation(views::LayoutOrientation::kHorizontal);
layout->SetMainAxisAlignment(views::LayoutAlignment::kCenter);
layout->SetCrossAxisAlignment(views::LayoutAlignment::kStretch);
auto camera = std::make_unique<views::ImageView>();
camera->SetImage(ui::ImageModel::FromVectorIcon(kPrivacyIndicatorsCameraIcon,
kColorAshIconColorPrimary));
AddChildView(std::move(camera));
auto microphone = std::make_unique<views::ImageView>();
microphone->SetImage(ui::ImageModel::FromVectorIcon(
kPrivacyIndicatorsMicrophoneIcon, kColorAshIconColorPrimary));
AddChildView(std::move(microphone));
AddChildView(std::make_unique<views::Label>(u"Meet"));
SetBorder(views::CreateEmptyBorder(
gfx::Insets::VH(kBorderInsetDimension, kBorderInsetDimension)));
SetBackground(views::CreateRoundedRectBackground(
gfx::kGoogleBlue800, kBackgroundRoundedRectRadius));
}
} // namespace ash::video_conference

@ -1,26 +0,0 @@
// Copyright 2022 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_VIDEO_CONFERENCE_BUBBLE_RETURN_TO_APP_BUTTON_H_
#define ASH_SYSTEM_VIDEO_CONFERENCE_BUBBLE_RETURN_TO_APP_BUTTON_H_
#include "ui/views/view.h"
namespace ash::video_conference {
// The "return to app" button that resides in the video conference bubble. The
// user selects from a list of apps that are actively capturing audio/video
// and/or sharing the screen, and the selected app is brought to the top and
// focused.
class ReturnToAppButton : public views::View {
public:
ReturnToAppButton();
ReturnToAppButton(const ReturnToAppButton&) = delete;
ReturnToAppButton& operator=(const ReturnToAppButton&) = delete;
~ReturnToAppButton() override = default;
};
} // namespace ash::video_conference
#endif // ASH_SYSTEM_VIDEO_CONFERENCE_BUBBLE_RETURN_TO_APP_BUTTON_H_

@ -0,0 +1,171 @@
// Copyright 2022 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/video_conference/bubble/return_to_app_panel.h"
#include <memory>
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_id.h"
#include "ash/system/video_conference/bubble/bubble_view_ids.h"
#include "ash/system/video_conference/video_conference_tray_controller.h"
#include "base/strings/utf_string_conversions.h"
#include "chromeos/crosapi/mojom/video_conference.mojom.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/image_model.h"
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/flex_layout.h"
namespace ash::video_conference {
namespace {
const int kReturnToAppPanelRadius = 16;
const int kReturnToAppPanelSpacing = 8;
const int kReturnToAppButtonSpacing = 12;
const int kReturnToAppButtonIconsSpacing = 2;
// Creates a view containing camera, microphone, and screen share icons that
// shows capturing state of a media app.
std::unique_ptr<views::View> CreateReturnToAppIconsContainer(
bool is_capturing_camera,
bool is_capturing_microphone,
bool is_capturing_screen) {
auto container = std::make_unique<views::View>();
container->SetLayoutManager(std::make_unique<views::FlexLayout>())
->SetOrientation(views::LayoutOrientation::kHorizontal)
.SetMainAxisAlignment(views::LayoutAlignment::kCenter)
.SetCrossAxisAlignment(views::LayoutAlignment::kStretch)
.SetDefault(views::kMarginsKey,
gfx::Insets::TLBR(0, kReturnToAppButtonIconsSpacing / 2, 0,
kReturnToAppButtonIconsSpacing / 2));
if (is_capturing_camera) {
auto camera_icon = std::make_unique<views::ImageView>();
camera_icon->SetImage(ui::ImageModel::FromVectorIcon(
kPrivacyIndicatorsCameraIcon, cros_tokens::kCrosSysPositive));
container->AddChildView(std::move(camera_icon));
}
if (is_capturing_microphone) {
auto microphone_icon = std::make_unique<views::ImageView>();
microphone_icon->SetImage(ui::ImageModel::FromVectorIcon(
kPrivacyIndicatorsMicrophoneIcon, cros_tokens::kCrosSysPositive));
container->AddChildView(std::move(microphone_icon));
}
if (is_capturing_screen) {
auto screen_share_icon = std::make_unique<views::ImageView>();
screen_share_icon->SetImage(ui::ImageModel::FromVectorIcon(
kPrivacyIndicatorsScreenShareIcon, cros_tokens::kCrosSysPositive));
container->AddChildView(std::move(screen_share_icon));
}
return container;
}
// Gets the display text representing a media app shown in the return to app
// panel.
std::u16string GetMediaAppDisplayText(
mojo::StructPtr<crosapi::mojom::VideoConferenceMediaAppInfo>& media_app) {
// Displays the url if it is valid. Otherwise, display app title.
auto url = media_app->url;
return url && url->is_valid() ? base::UTF8ToUTF16(url->GetContent())
: media_app->title;
}
} // namespace
// -----------------------------------------------------------------------------
// ReturnToAppButton:
ReturnToAppButton::ReturnToAppButton(bool is_capturing_camera,
bool is_capturing_microphone,
bool is_capturing_screen,
const std::u16string& display_text)
: is_capturing_camera_(is_capturing_camera),
is_capturing_microphone_(is_capturing_microphone),
is_capturing_screen_(is_capturing_screen) {
SetLayoutManager(std::make_unique<views::FlexLayout>())
->SetOrientation(views::LayoutOrientation::kHorizontal)
.SetMainAxisAlignment(views::LayoutAlignment::kCenter)
.SetCrossAxisAlignment(views::LayoutAlignment::kStretch)
.SetDefault(views::kMarginsKey,
gfx::Insets::TLBR(0, kReturnToAppButtonSpacing / 2, 0,
kReturnToAppButtonSpacing / 2));
AddChildView(CreateReturnToAppIconsContainer(
is_capturing_camera, is_capturing_microphone, is_capturing_screen));
label_ = AddChildView(std::make_unique<views::Label>(display_text));
}
// -----------------------------------------------------------------------------
// ReturnToAppPanel:
ReturnToAppPanel::ReturnToAppPanel() {
SetID(BubbleViewID::kReturnToApp);
SetLayoutManager(std::make_unique<views::FlexLayout>())
->SetOrientation(views::LayoutOrientation::kVertical)
.SetMainAxisAlignment(views::LayoutAlignment::kCenter)
.SetCrossAxisAlignment(views::LayoutAlignment::kStretch)
.SetDefault(views::kMarginsKey,
gfx::Insets::TLBR(0, 0, kReturnToAppPanelSpacing, 0))
.SetInteriorMargin(gfx::Insets::TLBR(12, 0, 0, 0));
// Add running media apps buttons to the panel.
VideoConferenceTrayController::Get()->GetMediaApps(base::BindOnce(
&ReturnToAppPanel::AddButtonsToPanel, weak_ptr_factory_.GetWeakPtr()));
SetBackground(views::CreateThemedRoundedRectBackground(
cros_tokens::kCrosSysSystemOnBase, kReturnToAppPanelRadius));
}
ReturnToAppPanel::~ReturnToAppPanel() = default;
void ReturnToAppPanel::AddButtonsToPanel(MediaApps apps) {
if (apps.size() < 1) {
SetVisible(false);
return;
}
if (apps.size() == 1) {
auto& app = apps.front();
AddChildView(std::make_unique<ReturnToAppButton>(
app->is_capturing_camera, app->is_capturing_microphone,
app->is_capturing_screen, GetMediaAppDisplayText(app)));
return;
}
bool any_apps_capturing_camera = false;
bool any_apps_capturing_microphone = false;
bool any_apps_capturing_screen = false;
for (auto& app : apps) {
AddChildView(std::make_unique<ReturnToAppButton>(
app->is_capturing_camera, app->is_capturing_microphone,
app->is_capturing_screen, GetMediaAppDisplayText(app)));
any_apps_capturing_camera |= app->is_capturing_camera;
any_apps_capturing_microphone |= app->is_capturing_microphone;
any_apps_capturing_screen |= app->is_capturing_screen;
}
auto summary_text = l10n_util::GetStringFUTF16Int(
IDS_ASH_VIDEO_CONFERENCE_RETURN_TO_APP_SUMMARY_TEXT,
static_cast<int>(apps.size()));
AddChildViewAt(std::make_unique<ReturnToAppButton>(
any_apps_capturing_camera, any_apps_capturing_microphone,
any_apps_capturing_screen, summary_text),
0);
}
} // namespace ash::video_conference

@ -0,0 +1,76 @@
// Copyright 2022 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_VIDEO_CONFERENCE_BUBBLE_RETURN_TO_APP_PANEL_H_
#define ASH_SYSTEM_VIDEO_CONFERENCE_BUBBLE_RETURN_TO_APP_PANEL_H_
#include <string>
#include "ash/ash_export.h"
#include "base/memory/weak_ptr.h"
#include "chromeos/crosapi/mojom/video_conference.mojom-forward.h"
#include "ui/views/view.h"
namespace views {
class Label;
} // namespace views
namespace ash::video_conference {
using MediaApps = std::vector<crosapi::mojom::VideoConferenceMediaAppInfoPtr>;
// The "return to app" button that resides within the "return to app" panel,
// showing information of a particular running media app. Clicking on this
// button will take users to the app.
class ASH_EXPORT ReturnToAppButton : public views::View {
public:
ReturnToAppButton(bool is_capturing_camera,
bool is_capturing_microphone,
bool is_capturing_screen,
const std::u16string& display_text);
ReturnToAppButton(const ReturnToAppButton&) = delete;
ReturnToAppButton& operator=(const ReturnToAppButton&) = delete;
~ReturnToAppButton() override = default;
bool is_capturing_camera() const { return is_capturing_camera_; }
bool is_capturing_microphone() const { return is_capturing_microphone_; }
bool is_capturing_screen() const { return is_capturing_screen_; }
views::Label* label() { return label_; }
private:
// Indicates if the running app is using camera, microphone, or screen
// sharing.
const bool is_capturing_camera_;
const bool is_capturing_microphone_;
const bool is_capturing_screen_;
// Label showing the url or name of the running app. Owned by the views
// hierarchy.
views::Label* label_ = nullptr;
};
// The "return to app" panel that resides in the video conference bubble. The
// user selects from a list of apps that are actively capturing audio/video
// and/or sharing the screen, and the selected app is brought to the top and
// focused.
class ASH_EXPORT ReturnToAppPanel : public views::View {
public:
ReturnToAppPanel();
ReturnToAppPanel(const ReturnToAppPanel&) = delete;
ReturnToAppPanel& operator=(const ReturnToAppPanel&) = delete;
~ReturnToAppPanel() override;
private:
// Used by the ctor to add `ReturnToAppButton`(s) to the panel.
void AddButtonsToPanel(MediaApps apps);
base::WeakPtrFactory<ReturnToAppPanel> weak_ptr_factory_{this};
};
} // namespace ash::video_conference
#endif // ASH_SYSTEM_VIDEO_CONFERENCE_BUBBLE_RETURN_TO_APP_PANEL_H_

@ -0,0 +1,158 @@
// Copyright 2022 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/video_conference/bubble/return_to_app_panel.h"
#include <memory>
#include "ash/constants/ash_features.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/video_conference/fake_video_conference_tray_controller.h"
#include "ash/test/ash_test_base.h"
#include "base/test/scoped_feature_list.h"
#include "chromeos/crosapi/mojom/video_conference.mojom.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/views/controls/label.h"
namespace {
// Verifies the information of `ReturnToAppButton`.
void VerifyReturnToAppButtonInfo(
ash::video_conference::ReturnToAppButton* button,
bool is_capturing_camera,
bool is_capturing_microphone,
bool is_capturing_screen,
const std::u16string& display_text) {
EXPECT_EQ(is_capturing_camera, button->is_capturing_camera());
EXPECT_EQ(is_capturing_microphone, button->is_capturing_microphone());
EXPECT_EQ(is_capturing_screen, button->is_capturing_screen());
EXPECT_EQ(display_text, button->label()->GetText());
}
// Used for verifying displayed url.
const std::string kGoogleMeetTestUrl = "https://meet.google.com/abc-xyz/ab-123";
const std::u16string kExpectedGoogleMeetDisplayedUrl =
u"meet.google.com/abc-xyz/ab-123";
} // namespace
namespace ash::video_conference {
class ReturnToAppPanelTest : public AshTestBase {
public:
ReturnToAppPanelTest() = default;
ReturnToAppPanelTest(const ReturnToAppPanelTest&) = delete;
ReturnToAppPanelTest& operator=(const ReturnToAppPanelTest&) = delete;
~ReturnToAppPanelTest() override = default;
// AshTestBase:
void SetUp() override {
scoped_feature_list_.InitAndEnableFeature(features::kVcControlsUi);
// Here we have to create the global instance of `CrasAudioHandler` before
// `FakeVideoConferenceTrayController`, so we do it here and not do it in
// `AshTestBase`.
CrasAudioClient::InitializeFake();
CrasAudioHandler::InitializeForTesting();
// Instantiates a fake controller (the real one is created in
// ChromeBrowserMainExtraPartsAsh::PreProfileInit() which is not called in
// ash unit tests).
controller_ = std::make_unique<FakeVideoConferenceTrayController>();
set_create_global_cras_audio_handler(false);
AshTestBase::SetUp();
}
void TearDown() override {
AshTestBase::TearDown();
controller_.reset();
CrasAudioHandler::Shutdown();
CrasAudioClient::Shutdown();
}
FakeVideoConferenceTrayController* controller() { return controller_.get(); }
private:
base::test::ScopedFeatureList scoped_feature_list_;
std::unique_ptr<FakeVideoConferenceTrayController> controller_;
};
TEST_F(ReturnToAppPanelTest, NoApp) {
controller()->ClearMediaApps();
// The view should not be visible when there's no app.
auto return_to_app_panel = std::make_unique<ReturnToAppPanel>();
EXPECT_FALSE(return_to_app_panel->GetVisible());
}
TEST_F(ReturnToAppPanelTest, OneApp) {
bool is_capturing_camera = true;
bool is_capturing_microphone = false;
bool is_capturing_screen = false;
controller()->ClearMediaApps();
controller()->AddMediaApp(crosapi::mojom::VideoConferenceMediaAppInfo::New(
/*id=*/base::UnguessableToken::Create(),
/*last_activity_time=*/base::Time::Now(), is_capturing_camera,
is_capturing_microphone, is_capturing_screen, /*title=*/u"Google Meet",
/*url=*/GURL(kGoogleMeetTestUrl)));
// There should be one child representing the only one running media app.
auto return_to_app_panel = std::make_unique<ReturnToAppPanel>();
EXPECT_EQ(1u, return_to_app_panel->children().size());
auto* app_button =
static_cast<ReturnToAppButton*>(return_to_app_panel->children().front());
VerifyReturnToAppButtonInfo(app_button, is_capturing_camera,
is_capturing_microphone, is_capturing_screen,
kExpectedGoogleMeetDisplayedUrl);
}
TEST_F(ReturnToAppPanelTest, MultipleApps) {
controller()->ClearMediaApps();
controller()->AddMediaApp(crosapi::mojom::VideoConferenceMediaAppInfo::New(
/*id=*/base::UnguessableToken::Create(),
/*last_activity_time=*/base::Time::Now(),
/*is_capturing_camera=*/true, /*is_capturing_microphone=*/false,
/*is_capturing_screen=*/false, /*title=*/u"Google Meet",
/*url=*/GURL(kGoogleMeetTestUrl)));
controller()->AddMediaApp(crosapi::mojom::VideoConferenceMediaAppInfo::New(
/*id=*/base::UnguessableToken::Create(),
/*last_activity_time=*/base::Time::Now(),
/*is_capturing_camera=*/false, /*is_capturing_microphone=*/true,
/*is_capturing_screen=*/true, /*title=*/u"Zoom",
/*url=*/absl::nullopt));
// There should be three children, one representing the summary row and two
// for two running media apps.
auto return_to_app_panel = std::make_unique<ReturnToAppPanel>();
EXPECT_EQ(3u, return_to_app_panel->children().size());
// The first row should be the summary row, representing the state of
// capturing from all apps and showing that 2 apps are running.
auto* summary_row =
static_cast<ReturnToAppButton*>(return_to_app_panel->children().front());
VerifyReturnToAppButtonInfo(
summary_row, /*is_capturing_camera=*/true,
/*is_capturing_microphone=*/true,
/*is_capturing_screen=*/true,
l10n_util::GetStringFUTF16Int(
IDS_ASH_VIDEO_CONFERENCE_RETURN_TO_APP_SUMMARY_TEXT, 2));
// Verify the next 2 rows, representing the 2 running apps.
auto* first_app_row =
static_cast<ReturnToAppButton*>(return_to_app_panel->children()[1]);
VerifyReturnToAppButtonInfo(first_app_row, /*is_capturing_camera=*/true,
/*is_capturing_microphone=*/false,
/*is_capturing_screen=*/false,
kExpectedGoogleMeetDisplayedUrl);
// If the url is not provided, the button should display the app title.
auto* second_app_row =
static_cast<ReturnToAppButton*>(return_to_app_panel->children()[2]);
VerifyReturnToAppButtonInfo(second_app_row, /*is_capturing_camera=*/false,
/*is_capturing_microphone=*/true,
/*is_capturing_screen=*/true, u"Zoom");
}
} // namespace ash::video_conference

@ -5,15 +5,32 @@
#include "ash/system/video_conference/fake_video_conference_tray_controller.h"
#include "ash/system/video_conference/effects/fake_video_conference_effects.h"
#include "ash/system/video_conference/video_conference_tray_controller.h"
#include "base/functional/callback.h"
#include "chromeos/ash/components/audio/cras_audio_handler.h"
#include "chromeos/crosapi/mojom/video_conference.mojom.h"
#include "media/capture/video/chromeos/mojom/cros_camera_service.mojom-shared.h"
#include "url/gurl.h"
namespace ash {
FakeVideoConferenceTrayController::FakeVideoConferenceTrayController()
: effect_repository_(
std::make_unique<fake_video_conference::EffectRepository>(
/*controller=*/this)) {}
/*controller=*/this)) {
AddMediaApp(crosapi::mojom::VideoConferenceMediaAppInfo::New(
/*id=*/base::UnguessableToken::Create(),
/*last_activity_time=*/base::Time::Now(),
/*is_capturing_camera=*/true, /*is_capturing_microphone=*/false,
/*is_capturing_screen=*/false, /*title=*/u"Google Meet",
/*url=*/GURL("https://meet.google.com/abc-xyz/ab-123")));
AddMediaApp(crosapi::mojom::VideoConferenceMediaAppInfo::New(
/*id=*/base::UnguessableToken::Create(),
/*last_activity_time=*/base::Time::Now(),
/*is_capturing_camera=*/false, /*is_capturing_microphone=*/true,
/*is_capturing_screen=*/true, /*title=*/u"Zoom",
/*url=*/absl::nullopt));
}
FakeVideoConferenceTrayController::~FakeVideoConferenceTrayController() {
effect_repository_.reset();
@ -32,4 +49,22 @@ void FakeVideoConferenceTrayController::SetMicrophoneMuted(bool muted) {
CrasAudioHandler::InputMuteChangeMethod::kKeyboardButton);
}
void FakeVideoConferenceTrayController::GetMediaApps(
base::OnceCallback<void(MediaApps)> ui_callback) {
MediaApps apps;
for (auto& app : media_apps_) {
apps.push_back(app->Clone());
}
std::move(ui_callback).Run(std::move(apps));
}
void FakeVideoConferenceTrayController::AddMediaApp(
crosapi::mojom::VideoConferenceMediaAppInfoPtr media_app) {
media_apps_.push_back(std::move(media_app));
}
void FakeVideoConferenceTrayController::ClearMediaApps() {
media_apps_.clear();
}
} // namespace ash

@ -31,11 +31,19 @@ class ASH_EXPORT FakeVideoConferenceTrayController
// VideoConferenceTrayController:
void SetCameraMuted(bool muted) override;
void SetMicrophoneMuted(bool muted) override;
void GetMediaApps(base::OnceCallback<void(MediaApps)> ui_callback) override;
// Adds or clears media app(s) in `media_apps_`.
void AddMediaApp(crosapi::mojom::VideoConferenceMediaAppInfoPtr media_app);
void ClearMediaApps();
bool camera_muted() { return camera_muted_; }
bool microphone_muted() { return microphone_muted_; }
private:
// A vector containing all currently running media apps. Used for testing.
MediaApps media_apps_;
// Indicates whether camera/microphone is muted.
bool camera_muted_ = false;
bool microphone_muted_ = false;

@ -15,6 +15,8 @@
namespace ash {
using MediaApps = std::vector<crosapi::mojom::VideoConferenceMediaAppInfoPtr>;
// Controller that will act as a "bridge" between VC apps management and the VC
// UI layers. The singleton instance is constructed immediately before and
// destructed immediately after the UI, so any code that keeps a reference to
@ -69,6 +71,11 @@ class ASH_EXPORT VideoConferenceTrayController
// Sets the state for microphone mute. Virtual for testing/mocking.
virtual void SetMicrophoneMuted(bool muted) = 0;
// Returns asynchronously a vector of media apps that will be displayed in the
// "Return to app" panel of the bubble. Virtual for testing/mocking.
virtual void GetMediaApps(
base::OnceCallback<void(MediaApps)> ui_callback) = 0;
// Updates the tray UI with the given `VideoConferenceMediaState`.
void UpdateWithMediaState(VideoConferenceMediaState state);

@ -7,6 +7,9 @@
#include "ash/constants/ash_pref_names.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "chrome/browser/ash/crosapi/crosapi_ash.h"
#include "chrome/browser/ash/crosapi/crosapi_manager.h"
#include "chrome/browser/ash/video_conference/video_conference_manager_ash.h"
#include "components/prefs/pref_service.h"
namespace ash {
@ -35,4 +38,12 @@ void VideoConferenceTrayControllerImpl::SetMicrophoneMuted(bool muted) {
pref_service->SetBoolean(prefs::kUserMicrophoneAllowed, !muted);
}
void VideoConferenceTrayControllerImpl::GetMediaApps(
base::OnceCallback<void(MediaApps)> ui_callback) {
crosapi::CrosapiManager::Get()
->crosapi_ash()
->video_conference_manager_ash()
->GetMediaApps(std::move(ui_callback));
}
} // namespace ash

@ -24,6 +24,7 @@ class VideoConferenceTrayControllerImpl : public VideoConferenceTrayController {
// VideoConferenceTrayController:
void SetCameraMuted(bool muted) override;
void SetMicrophoneMuted(bool muted) override;
void GetMediaApps(base::OnceCallback<void(MediaApps)> ui_callback) override;
};
} // namespace ash