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:

committed by
Chromium LUCI CQ

parent
303fd02c2e
commit
2dd64a9997
@ -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_
|
171
ash/system/video_conference/bubble/return_to_app_panel.cc
Normal file
171
ash/system/video_conference/bubble/return_to_app_panel.cc
Normal file
@ -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
|
76
ash/system/video_conference/bubble/return_to_app_panel.h
Normal file
76
ash/system/video_conference/bubble/return_to_app_panel.h
Normal file
@ -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
|
||||
|
Reference in New Issue
Block a user