camera_effects: Host background blur effect
CameraEffectsController hosts the camera background blur effect, exposing it via the VC bubble. Bug: b/259585295 Change-Id: Ife30062e8482f094899a929e5506766a09455ba4 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4119258 Commit-Queue: Roger Tinkoff <rtinkoff@google.com> Reviewed-by: Alex Newcomer <newcomer@chromium.org> Cr-Commit-Position: refs/heads/main@{#1088880}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
5d243e2d01
commit
473ee66968
ash
ash_strings.grd
ash_strings_grd
IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_HEAVY.png.sha1IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_LIGHT.png.sha1IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_LOWEST.png.sha1IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_MAXIMUM.png.sha1IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_MEDIUM.png.sha1IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_NAME.png.sha1IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_OFF.png.sha1
system
camera
video_conference
@@ -1447,13 +1447,34 @@ Style notes:
|
|||||||
Scanning
|
Scanning
|
||||||
</message>
|
</message>
|
||||||
|
|
||||||
<!-- Video Conference tray-->
|
<!-- Video Conference tray/bubble -->
|
||||||
<message name="IDS_ASH_VIDEO_CONFERENCE_TOGGLE_BUBBLE_BUTTON_TOOLTIP" desc="The tooltip for the toggle bubble button in the video conference tray.">
|
<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
|
Camera and audio controls
|
||||||
</message>
|
</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.">
|
<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
|
Used by <ph name="APP_COUNT">$1<ex>2</ex></ph> apps
|
||||||
</message>
|
</message>
|
||||||
|
<message name="IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_NAME" desc="Text for name of the background blur effect in the VC bubble.">
|
||||||
|
Background Blur
|
||||||
|
</message>
|
||||||
|
<message name="IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_OFF" desc="Text for background blur 'off' setting, in the VC bubble.">
|
||||||
|
Off
|
||||||
|
</message>
|
||||||
|
<message name="IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_LOWEST" desc="Text for background blur 'lowest' setting, in the VC bubble.">
|
||||||
|
Lowest
|
||||||
|
</message>
|
||||||
|
<message name="IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_LIGHT" desc="Text for background blur 'light' setting, in the VC bubble.">
|
||||||
|
Light
|
||||||
|
</message>
|
||||||
|
<message name="IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_MEDIUM" desc="Text for background blur 'medium' setting, in the VC bubble.">
|
||||||
|
Medium
|
||||||
|
</message>
|
||||||
|
<message name="IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_HEAVY" desc="Text for background blur 'heavy' setting, in the VC bubble.">
|
||||||
|
Heavy
|
||||||
|
</message>
|
||||||
|
<message name="IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_MAXIMUM" desc="Text for background blur 'maximum' setting, in the VC bubble.">
|
||||||
|
Maximum
|
||||||
|
</message>
|
||||||
|
|
||||||
<!-- Phone Hub tray-->
|
<!-- 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.">
|
<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 @@
|
|||||||
|
6244582b0cd0ef43796e6ff6e74058f7d6e90517
|
@@ -0,0 +1 @@
|
|||||||
|
6244582b0cd0ef43796e6ff6e74058f7d6e90517
|
@@ -0,0 +1 @@
|
|||||||
|
c79c38b0c43dc23f280615a1baa9ab6b0a1159fe
|
@@ -0,0 +1 @@
|
|||||||
|
6244582b0cd0ef43796e6ff6e74058f7d6e90517
|
@@ -0,0 +1 @@
|
|||||||
|
6244582b0cd0ef43796e6ff6e74058f7d6e90517
|
@@ -0,0 +1 @@
|
|||||||
|
6244582b0cd0ef43796e6ff6e74058f7d6e90517
|
@@ -0,0 +1 @@
|
|||||||
|
6244582b0cd0ef43796e6ff6e74058f7d6e90517
|
@@ -5,23 +5,106 @@
|
|||||||
#include "ash/system/camera/camera_effects_controller.h"
|
#include "ash/system/camera/camera_effects_controller.h"
|
||||||
|
|
||||||
#include "ash/constants/ash_pref_names.h"
|
#include "ash/constants/ash_pref_names.h"
|
||||||
|
#include "ash/resources/vector_icons/vector_icons.h"
|
||||||
#include "ash/session/session_controller_impl.h"
|
#include "ash/session/session_controller_impl.h"
|
||||||
#include "ash/shell.h"
|
#include "ash/shell.h"
|
||||||
|
#include "ash/strings/grit/ash_strings.h"
|
||||||
|
#include "ash/system/video_conference/effects/video_conference_tray_effects_manager.h"
|
||||||
|
#include "ash/system/video_conference/effects/video_conference_tray_effects_manager_types.h"
|
||||||
|
#include "ash/system/video_conference/video_conference_tray_controller.h"
|
||||||
#include "base/check_is_test.h"
|
#include "base/check_is_test.h"
|
||||||
|
#include "base/notreached.h"
|
||||||
#include "base/task/bind_post_task.h"
|
#include "base/task/bind_post_task.h"
|
||||||
#include "base/task/single_thread_task_runner.h"
|
#include "base/task/single_thread_task_runner.h"
|
||||||
#include "components/prefs/pref_change_registrar.h"
|
#include "components/prefs/pref_change_registrar.h"
|
||||||
#include "components/prefs/pref_registry_simple.h"
|
#include "components/prefs/pref_registry_simple.h"
|
||||||
#include "components/prefs/pref_service.h"
|
#include "components/prefs/pref_service.h"
|
||||||
#include "media/capture/video/chromeos/camera_hal_dispatcher_impl.h"
|
#include "media/capture/video/chromeos/camera_hal_dispatcher_impl.h"
|
||||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
#include "ui/base/l10n/l10n_util.h"
|
||||||
|
|
||||||
namespace ash {
|
namespace ash {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// The value stored in pref to indicate that background blur is disabled.
|
// A `std::pair` representation of the background blur state that
|
||||||
constexpr int kBackgroundBlurLevelForDisabling = -1;
|
// `CameraHalDispatcherImpl` expects:
|
||||||
|
// - `BlurLevel` that specifies how much blur to apply
|
||||||
|
// - `bool` that's 'true' if background blur is enabled, false otherwise
|
||||||
|
using CameraHalBackgroundBlurState = std::pair<cros::mojom::BlurLevel, bool>;
|
||||||
|
|
||||||
|
// Returns 'true' if `pref_value` is an allowable value of
|
||||||
|
// `CameraEffectsController::BackgroundBlurEffectState`, 'false' otherwise.
|
||||||
|
bool IsValidBackgroundBlurState(int pref_value) {
|
||||||
|
switch (pref_value) {
|
||||||
|
case CameraEffectsController::BackgroundBlurEffectState::kOff:
|
||||||
|
case CameraEffectsController::BackgroundBlurEffectState::kLowest:
|
||||||
|
case CameraEffectsController::BackgroundBlurEffectState::kLight:
|
||||||
|
case CameraEffectsController::BackgroundBlurEffectState::kMedium:
|
||||||
|
case CameraEffectsController::BackgroundBlurEffectState::kHeavy:
|
||||||
|
case CameraEffectsController::BackgroundBlurEffectState::kMaximum:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maps `effect_state` (assumed to be a value read out of
|
||||||
|
// `prefs::kBackgroundBlur`) to a `CameraHalBackgroundBlurState` (that
|
||||||
|
// `CameraHalDispatcherImpl` expects).
|
||||||
|
CameraHalBackgroundBlurState MapBackgroundBlurEffectStateToCameraHalState(
|
||||||
|
int effect_state) {
|
||||||
|
DCHECK(IsValidBackgroundBlurState(effect_state));
|
||||||
|
|
||||||
|
switch (effect_state) {
|
||||||
|
// For state `kOff`, the `bool` is 'false' because background blur is
|
||||||
|
// disabled, `BlurLevel` is set to `kLowest` but its value doesn't matter.
|
||||||
|
case CameraEffectsController::BackgroundBlurEffectState::kOff:
|
||||||
|
return std::make_pair(cros::mojom::BlurLevel::kLowest, false);
|
||||||
|
|
||||||
|
// For states other than `kOff`, background blur is enabled so the `bool`
|
||||||
|
// is set to 'true' and `effect_state` is mapped to a `BlurLevel`.
|
||||||
|
case CameraEffectsController::BackgroundBlurEffectState::kLowest:
|
||||||
|
return std::make_pair(cros::mojom::BlurLevel::kLowest, true);
|
||||||
|
case CameraEffectsController::BackgroundBlurEffectState::kLight:
|
||||||
|
return std::make_pair(cros::mojom::BlurLevel::kLight, true);
|
||||||
|
case CameraEffectsController::BackgroundBlurEffectState::kMedium:
|
||||||
|
return std::make_pair(cros::mojom::BlurLevel::kMedium, true);
|
||||||
|
case CameraEffectsController::BackgroundBlurEffectState::kHeavy:
|
||||||
|
return std::make_pair(cros::mojom::BlurLevel::kHeavy, true);
|
||||||
|
case CameraEffectsController::BackgroundBlurEffectState::kMaximum:
|
||||||
|
return std::make_pair(cros::mojom::BlurLevel::kMaximum, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
NOTREACHED();
|
||||||
|
return std::make_pair(cros::mojom::BlurLevel::kLowest, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maps the `CameraHalDispatcherImpl`-ready background blur state
|
||||||
|
// `level`/`enabled` to `CameraEffectsController::BackgroundBlurEffectState`,
|
||||||
|
// which is what's written to `prefs::kBackgroundBlur`.
|
||||||
|
CameraEffectsController::BackgroundBlurEffectState
|
||||||
|
MapBackgroundBlurCameraHalStateToEffectState(cros::mojom::BlurLevel level,
|
||||||
|
bool enabled) {
|
||||||
|
if (!enabled) {
|
||||||
|
return CameraEffectsController::BackgroundBlurEffectState::kOff;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (level) {
|
||||||
|
case cros::mojom::BlurLevel::kLowest:
|
||||||
|
return CameraEffectsController::BackgroundBlurEffectState::kLowest;
|
||||||
|
case cros::mojom::BlurLevel::kLight:
|
||||||
|
return CameraEffectsController::BackgroundBlurEffectState::kLight;
|
||||||
|
case cros::mojom::BlurLevel::kMedium:
|
||||||
|
return CameraEffectsController::BackgroundBlurEffectState::kMedium;
|
||||||
|
case cros::mojom::BlurLevel::kHeavy:
|
||||||
|
return CameraEffectsController::BackgroundBlurEffectState::kHeavy;
|
||||||
|
case cros::mojom::BlurLevel::kMaximum:
|
||||||
|
return CameraEffectsController::BackgroundBlurEffectState::kMaximum;
|
||||||
|
}
|
||||||
|
|
||||||
|
NOTREACHED();
|
||||||
|
return CameraEffectsController::BackgroundBlurEffectState::kLowest;
|
||||||
|
}
|
||||||
|
|
||||||
// Gets blur level from chrome flag.
|
// Gets blur level from chrome flag.
|
||||||
cros::mojom::BlurLevel GetBlurLevelFromFlag() {
|
cros::mojom::BlurLevel GetBlurLevelFromFlag() {
|
||||||
@@ -83,7 +166,15 @@ CameraEffectsController::CameraEffectsController() {
|
|||||||
weak_factory_.GetWeakPtr())));
|
weak_factory_.GetWeakPtr())));
|
||||||
}
|
}
|
||||||
|
|
||||||
CameraEffectsController::~CameraEffectsController() = default;
|
CameraEffectsController::~CameraEffectsController() {
|
||||||
|
VideoConferenceTrayEffectsManager& effects_manager =
|
||||||
|
VideoConferenceTrayController::Get()->effects_manager();
|
||||||
|
if (effects_manager.IsDelegateRegistered(this)) {
|
||||||
|
// The `VcEffectsDelegate` was registered, so must therefore be
|
||||||
|
// unregistered.
|
||||||
|
effects_manager.UnregisterDelegate(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool CameraEffectsController::IsCameraEffectsSupported(
|
bool CameraEffectsController::IsCameraEffectsSupported(
|
||||||
cros::mojom::CameraEffect effect) {
|
cros::mojom::CameraEffect effect) {
|
||||||
@@ -118,15 +209,16 @@ void CameraEffectsController::RemoveObserver(Observer* observer) {
|
|||||||
// static
|
// static
|
||||||
void CameraEffectsController::RegisterProfilePrefs(
|
void CameraEffectsController::RegisterProfilePrefs(
|
||||||
PrefRegistrySimple* registry) {
|
PrefRegistrySimple* registry) {
|
||||||
if (!IsCameraEffectsSupported())
|
if (!IsCameraEffectsSupported()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// We have to register all camera effects prefs; because we need use them to
|
// We have to register all camera effects prefs; because we need use them to
|
||||||
// construct the cros::mojom::EffectsConfigPtr.
|
// construct the cros::mojom::EffectsConfigPtr.
|
||||||
registry->RegisterIntegerPref(prefs::kBackgroundBlur,
|
registry->RegisterIntegerPref(prefs::kBackgroundBlur,
|
||||||
GetInitialCameraEffects()->blur_enabled
|
GetInitialCameraEffects()->blur_enabled
|
||||||
? static_cast<int>(GetBlurLevelFromFlag())
|
? static_cast<int>(GetBlurLevelFromFlag())
|
||||||
: kBackgroundBlurLevelForDisabling);
|
: BackgroundBlurEffectState::kOff);
|
||||||
|
|
||||||
registry->RegisterBooleanPref(prefs::kBackgroundReplace,
|
registry->RegisterBooleanPref(prefs::kBackgroundReplace,
|
||||||
GetInitialCameraEffects()->replace_enabled);
|
GetInitialCameraEffects()->replace_enabled);
|
||||||
@@ -137,8 +229,10 @@ void CameraEffectsController::RegisterProfilePrefs(
|
|||||||
|
|
||||||
void CameraEffectsController::OnActiveUserPrefServiceChanged(
|
void CameraEffectsController::OnActiveUserPrefServiceChanged(
|
||||||
PrefService* pref_service) {
|
PrefService* pref_service) {
|
||||||
if (pref_change_registrar_ && pref_service == pref_change_registrar_->prefs())
|
if (pref_change_registrar_ &&
|
||||||
|
pref_service == pref_change_registrar_->prefs()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Initial login and user switching in multi profiles.
|
// Initial login and user switching in multi profiles.
|
||||||
pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
|
pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
|
||||||
@@ -167,6 +261,50 @@ void CameraEffectsController::OnActiveUserPrefServiceChanged(
|
|||||||
// If the camera has started, it won't get the previous setting so call it
|
// If the camera has started, it won't get the previous setting so call it
|
||||||
// here too. If the camera service isn't ready it this call will be ignored.
|
// here too. If the camera service isn't ready it this call will be ignored.
|
||||||
SetCameraEffects(GetEffectsConfigFromPref());
|
SetCameraEffects(GetEffectsConfigFromPref());
|
||||||
|
|
||||||
|
// If any effects have controls the user can access, this will create the
|
||||||
|
// effects UI and register `CameraEffectsController`'s `VcEffectsDelegate`
|
||||||
|
// interface.
|
||||||
|
InitializeEffectControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
int CameraEffectsController::GetEffectState(int effect_id) {
|
||||||
|
switch (effect_id) {
|
||||||
|
case static_cast<int>(cros::mojom::CameraEffect::kBackgroundBlur):
|
||||||
|
DCHECK(pref_change_registrar_ && pref_change_registrar_->prefs());
|
||||||
|
|
||||||
|
int pref_value =
|
||||||
|
pref_change_registrar_->prefs()->GetInteger(prefs::kBackgroundBlur);
|
||||||
|
if (!IsValidBackgroundBlurState(pref_value)) {
|
||||||
|
LOG(ERROR)
|
||||||
|
<< __FUNCTION__ << " value " << pref_value
|
||||||
|
<< " is NOT a valid background blur effect state, returning kOff";
|
||||||
|
return static_cast<int>(BackgroundBlurEffectState::kOff);
|
||||||
|
}
|
||||||
|
return pref_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return VcEffectState::kUnusedId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraEffectsController::OnEffectControlActivated(int effect_id,
|
||||||
|
int value) {
|
||||||
|
switch (effect_id) {
|
||||||
|
case static_cast<int>(cros::mojom::CameraEffect::kBackgroundBlur):
|
||||||
|
DCHECK(pref_change_registrar_ && pref_change_registrar_->prefs());
|
||||||
|
if (!IsValidBackgroundBlurState(value)) {
|
||||||
|
LOG(ERROR) << __FUNCTION__ << " value " << value
|
||||||
|
<< " is not a valid background blur effect state";
|
||||||
|
pref_change_registrar_->prefs()->SetInteger(
|
||||||
|
prefs::kBackgroundBlur,
|
||||||
|
static_cast<int>(BackgroundBlurEffectState::kOff));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pref_change_registrar_->prefs()->SetInteger(prefs::kBackgroundBlur,
|
||||||
|
value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CameraEffectsController::OnCameraEffectsPrefChanged(
|
void CameraEffectsController::OnCameraEffectsPrefChanged(
|
||||||
@@ -264,16 +402,21 @@ void CameraEffectsController::OnNewCameraEffectsSet(
|
|||||||
cros::mojom::EffectsConfigPtr
|
cros::mojom::EffectsConfigPtr
|
||||||
CameraEffectsController::GetEffectsConfigFromPref() {
|
CameraEffectsController::GetEffectsConfigFromPref() {
|
||||||
cros::mojom::EffectsConfigPtr effects = cros::mojom::EffectsConfig::New();
|
cros::mojom::EffectsConfigPtr effects = cros::mojom::EffectsConfig::New();
|
||||||
const int background_level_in_pref =
|
int background_blur_state_in_pref =
|
||||||
pref_change_registrar_->prefs()->GetInteger(prefs::kBackgroundBlur);
|
pref_change_registrar_->prefs()->GetInteger(prefs::kBackgroundBlur);
|
||||||
|
if (!IsValidBackgroundBlurState(background_blur_state_in_pref)) {
|
||||||
|
LOG(ERROR) << __FUNCTION__ << " background_blur_state_in_pref "
|
||||||
|
<< background_blur_state_in_pref
|
||||||
|
<< " is NOT a valid background blur effect state, using kOff";
|
||||||
|
background_blur_state_in_pref =
|
||||||
|
static_cast<int>(BackgroundBlurEffectState::kOff);
|
||||||
|
}
|
||||||
|
|
||||||
effects->blur_enabled =
|
CameraHalBackgroundBlurState blur_state =
|
||||||
background_level_in_pref != kBackgroundBlurLevelForDisabling;
|
MapBackgroundBlurEffectStateToCameraHalState(
|
||||||
|
background_blur_state_in_pref);
|
||||||
effects->blur_level =
|
effects->blur_enabled = blur_state.second;
|
||||||
effects->blur_enabled
|
effects->blur_level = blur_state.first;
|
||||||
? static_cast<cros::mojom::BlurLevel>(background_level_in_pref)
|
|
||||||
: GetBlurLevelFromFlag();
|
|
||||||
|
|
||||||
effects->replace_enabled =
|
effects->replace_enabled =
|
||||||
pref_change_registrar_->prefs()->GetBoolean(prefs::kBackgroundReplace);
|
pref_change_registrar_->prefs()->GetBoolean(prefs::kBackgroundReplace);
|
||||||
@@ -289,9 +432,9 @@ void CameraEffectsController::SetEffectsConfigToPref(
|
|||||||
if (new_config->blur_enabled != old_effects->blur_enabled ||
|
if (new_config->blur_enabled != old_effects->blur_enabled ||
|
||||||
new_config->blur_level != old_effects->blur_level) {
|
new_config->blur_level != old_effects->blur_level) {
|
||||||
pref_change_registrar_->prefs()->SetInteger(
|
pref_change_registrar_->prefs()->SetInteger(
|
||||||
prefs::kBackgroundBlur, new_config->blur_enabled
|
prefs::kBackgroundBlur,
|
||||||
? static_cast<int>(new_config->blur_level)
|
MapBackgroundBlurCameraHalStateToEffectState(new_config->blur_level,
|
||||||
: kBackgroundBlurLevelForDisabling);
|
new_config->blur_enabled));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_config->replace_enabled != old_effects->replace_enabled) {
|
if (new_config->replace_enabled != old_effects->replace_enabled) {
|
||||||
@@ -305,4 +448,100 @@ void CameraEffectsController::SetEffectsConfigToPref(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CameraEffectsController::IsEffectControlAvailable(
|
||||||
|
cros::mojom::CameraEffect effect /* = cros::mojom::CameraEffect::kNone*/) {
|
||||||
|
if (!ash::features::IsVcControlsUiEnabled()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (effect) {
|
||||||
|
case cros::mojom::CameraEffect::kNone:
|
||||||
|
// Return 'true' if any effect is available.
|
||||||
|
return IsCameraEffectsSupported(
|
||||||
|
cros::mojom::CameraEffect::kBackgroundBlur);
|
||||||
|
case cros::mojom::CameraEffect::kBackgroundBlur:
|
||||||
|
return IsCameraEffectsSupported(
|
||||||
|
cros::mojom::CameraEffect::kBackgroundBlur);
|
||||||
|
case cros::mojom::CameraEffect::kBackgroundReplace:
|
||||||
|
case cros::mojom::CameraEffect::kPortraitRelight:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraEffectsController::InitializeEffectControls() {
|
||||||
|
if (VideoConferenceTrayController::Get()
|
||||||
|
->effects_manager()
|
||||||
|
.IsDelegateRegistered(this)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If background blur UI controls are present, construct the effect and its
|
||||||
|
// states.
|
||||||
|
if (IsEffectControlAvailable(cros::mojom::CameraEffect::kBackgroundBlur)) {
|
||||||
|
auto effect = std::make_unique<VcHostedEffect>(
|
||||||
|
VcEffectType::kSetValue,
|
||||||
|
base::BindRepeating(
|
||||||
|
&CameraEffectsController::GetEffectState, base::Unretained(this),
|
||||||
|
static_cast<int>(cros::mojom::CameraEffect::kBackgroundBlur)));
|
||||||
|
effect->set_label_text(l10n_util::GetStringUTF16(
|
||||||
|
IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_NAME));
|
||||||
|
effect->set_id(
|
||||||
|
static_cast<int>(cros::mojom::CameraEffect::kBackgroundBlur));
|
||||||
|
AddBackgroundBlurStateToEffect(
|
||||||
|
effect.get(),
|
||||||
|
/*state_value=*/BackgroundBlurEffectState::kOff,
|
||||||
|
/*string_id=*/IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_OFF);
|
||||||
|
AddBackgroundBlurStateToEffect(
|
||||||
|
effect.get(),
|
||||||
|
/*state_value=*/BackgroundBlurEffectState::kLowest,
|
||||||
|
/*string_id=*/IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_LOWEST);
|
||||||
|
AddBackgroundBlurStateToEffect(
|
||||||
|
effect.get(),
|
||||||
|
/*state_value=*/BackgroundBlurEffectState::kLight,
|
||||||
|
/*string_id=*/IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_LIGHT);
|
||||||
|
AddBackgroundBlurStateToEffect(
|
||||||
|
effect.get(),
|
||||||
|
/*state_value=*/BackgroundBlurEffectState::kMedium,
|
||||||
|
/*string_id=*/IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_MEDIUM);
|
||||||
|
AddBackgroundBlurStateToEffect(
|
||||||
|
effect.get(),
|
||||||
|
/*state_value=*/BackgroundBlurEffectState::kHeavy,
|
||||||
|
/*string_id=*/IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_HEAVY);
|
||||||
|
AddBackgroundBlurStateToEffect(
|
||||||
|
effect.get(),
|
||||||
|
/*state_value=*/BackgroundBlurEffectState::kMaximum,
|
||||||
|
/*string_id=*/
|
||||||
|
IDS_ASH_VIDEO_CONFERENCE_BUBBLE_BACKGROUND_BLUR_MAXIMUM);
|
||||||
|
AddEffect(std::move(effect));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If *any* effects' UI controls are present, register with the effects
|
||||||
|
// manager.
|
||||||
|
if (IsEffectControlAvailable()) {
|
||||||
|
VideoConferenceTrayController::Get()->effects_manager().RegisterDelegate(
|
||||||
|
this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CameraEffectsController::AddBackgroundBlurStateToEffect(
|
||||||
|
VcHostedEffect* effect,
|
||||||
|
int state_value,
|
||||||
|
int string_id) {
|
||||||
|
DCHECK(effect);
|
||||||
|
effect->AddState(std::make_unique<VcEffectState>(
|
||||||
|
/*icon=*/nullptr,
|
||||||
|
/*label_text=*/l10n_util::GetStringUTF16(string_id),
|
||||||
|
/*accessible_name_id=*/string_id,
|
||||||
|
/*button_callback=*/
|
||||||
|
base::BindRepeating(
|
||||||
|
&CameraEffectsController::OnEffectControlActivated,
|
||||||
|
weak_factory_.GetWeakPtr(),
|
||||||
|
/*effect_id=*/
|
||||||
|
static_cast<int>(cros::mojom::CameraEffect::kBackgroundBlur),
|
||||||
|
/*value=*/state_value),
|
||||||
|
/*state=*/state_value));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ash
|
} // namespace ash
|
||||||
|
@@ -10,12 +10,12 @@
|
|||||||
#include "ash/ash_export.h"
|
#include "ash/ash_export.h"
|
||||||
#include "ash/public/cpp/session/session_controller.h"
|
#include "ash/public/cpp/session/session_controller.h"
|
||||||
#include "ash/public/cpp/session/session_observer.h"
|
#include "ash/public/cpp/session/session_observer.h"
|
||||||
|
#include "ash/system/video_conference/effects/video_conference_tray_effects_delegate.h"
|
||||||
#include "base/memory/weak_ptr.h"
|
#include "base/memory/weak_ptr.h"
|
||||||
#include "base/observer_list.h"
|
#include "base/observer_list.h"
|
||||||
#include "base/observer_list_types.h"
|
#include "base/observer_list_types.h"
|
||||||
#include "base/scoped_observation.h"
|
#include "base/scoped_observation.h"
|
||||||
#include "media/capture/video/chromeos/mojom/effects_pipeline.mojom.h"
|
#include "media/capture/video/chromeos/mojom/effects_pipeline.mojom.h"
|
||||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
|
||||||
|
|
||||||
class PrefRegistrySimple;
|
class PrefRegistrySimple;
|
||||||
class PrefService;
|
class PrefService;
|
||||||
@@ -25,7 +25,8 @@ namespace ash {
|
|||||||
|
|
||||||
// CameraEffectsController is the interface for any object in ash to
|
// CameraEffectsController is the interface for any object in ash to
|
||||||
// enable/change camera effects.
|
// enable/change camera effects.
|
||||||
class ASH_EXPORT CameraEffectsController : public SessionObserver {
|
class ASH_EXPORT CameraEffectsController : public SessionObserver,
|
||||||
|
public VcEffectsDelegate {
|
||||||
public:
|
public:
|
||||||
// Observer that will be notified on camera effects change.
|
// Observer that will be notified on camera effects change.
|
||||||
class Observer : public base::CheckedObserver {
|
class Observer : public base::CheckedObserver {
|
||||||
@@ -34,6 +35,17 @@ class ASH_EXPORT CameraEffectsController : public SessionObserver {
|
|||||||
cros::mojom::EffectsConfigPtr new_effects) = 0;
|
cros::mojom::EffectsConfigPtr new_effects) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Enum that represents the value persisted to `prefs::kBackgroundBlur`,
|
||||||
|
// which is the "ultimate source of truth" for the background blur setting.
|
||||||
|
enum BackgroundBlurEffectState {
|
||||||
|
kOff = -1,
|
||||||
|
kLowest = 0,
|
||||||
|
kLight = 1,
|
||||||
|
kMedium = 2,
|
||||||
|
kHeavy = 3,
|
||||||
|
kMaximum = 4,
|
||||||
|
};
|
||||||
|
|
||||||
CameraEffectsController();
|
CameraEffectsController();
|
||||||
|
|
||||||
CameraEffectsController(const CameraEffectsController&) = delete;
|
CameraEffectsController(const CameraEffectsController&) = delete;
|
||||||
@@ -48,6 +60,11 @@ class ASH_EXPORT CameraEffectsController : public SessionObserver {
|
|||||||
static bool IsCameraEffectsSupported(
|
static bool IsCameraEffectsSupported(
|
||||||
cros::mojom::CameraEffect effect = cros::mojom::CameraEffect::kNone);
|
cros::mojom::CameraEffect effect = cros::mojom::CameraEffect::kNone);
|
||||||
|
|
||||||
|
// Returns 'true' if UI controls for `effect` are available to the user,
|
||||||
|
// 'false' otherwise.
|
||||||
|
bool IsEffectControlAvailable(
|
||||||
|
cros::mojom::CameraEffect effect = cros::mojom::CameraEffect::kNone);
|
||||||
|
|
||||||
// Returns currently applied camera effects.
|
// Returns currently applied camera effects.
|
||||||
// Should only be called after user logs in.
|
// Should only be called after user logs in.
|
||||||
cros::mojom::EffectsConfigPtr GetCameraEffects();
|
cros::mojom::EffectsConfigPtr GetCameraEffects();
|
||||||
@@ -62,6 +79,10 @@ class ASH_EXPORT CameraEffectsController : public SessionObserver {
|
|||||||
// SessionObserver:
|
// SessionObserver:
|
||||||
void OnActiveUserPrefServiceChanged(PrefService* pref_service) override;
|
void OnActiveUserPrefServiceChanged(PrefService* pref_service) override;
|
||||||
|
|
||||||
|
// VcEffectsDelegate:
|
||||||
|
int GetEffectState(int effect_id) override;
|
||||||
|
void OnEffectControlActivated(int effect_id, int value) override;
|
||||||
|
|
||||||
void set_effect_result_for_testing(
|
void set_effect_result_for_testing(
|
||||||
cros::mojom::SetEffectResult effect_result_for_testing) {
|
cros::mojom::SetEffectResult effect_result_for_testing) {
|
||||||
effect_result_for_testing_ = effect_result_for_testing;
|
effect_result_for_testing_ = effect_result_for_testing;
|
||||||
@@ -89,6 +110,16 @@ class ASH_EXPORT CameraEffectsController : public SessionObserver {
|
|||||||
// Update prefs with the value in `config`.
|
// Update prefs with the value in `config`.
|
||||||
void SetEffectsConfigToPref(cros::mojom::EffectsConfigPtr config);
|
void SetEffectsConfigToPref(cros::mojom::EffectsConfigPtr config);
|
||||||
|
|
||||||
|
// Performs any initializations needed for effects whose controls are exposed
|
||||||
|
// via the UI.
|
||||||
|
void InitializeEffectControls();
|
||||||
|
|
||||||
|
// Adds a `std::unique_ptr<VcEffectState>` to `effect`, where `effect` is
|
||||||
|
// assumed to be that of camera background blur.
|
||||||
|
void AddBackgroundBlurStateToEffect(VcHostedEffect* effect,
|
||||||
|
int state_value,
|
||||||
|
int string_id);
|
||||||
|
|
||||||
// Used to bypass the CameraHalDispatcherImpl::SetCameraEffects for testing
|
// Used to bypass the CameraHalDispatcherImpl::SetCameraEffects for testing
|
||||||
// purpose. The value will be null for non-testing cases; and not null in
|
// purpose. The value will be null for non-testing cases; and not null in
|
||||||
// testing cases.
|
// testing cases.
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
#include "ash/constants/ash_pref_names.h"
|
#include "ash/constants/ash_pref_names.h"
|
||||||
#include "ash/session/session_controller_impl.h"
|
#include "ash/session/session_controller_impl.h"
|
||||||
#include "ash/shell.h"
|
#include "ash/shell.h"
|
||||||
|
#include "ash/system/video_conference/fake_video_conference_tray_controller.h"
|
||||||
#include "ash/test/ash_test_base.h"
|
#include "ash/test/ash_test_base.h"
|
||||||
#include "base/test/scoped_feature_list.h"
|
#include "base/test/scoped_feature_list.h"
|
||||||
|
|
||||||
@@ -37,9 +38,22 @@ class CameraEffectsControllerTest : public NoSessionAshTestBase {
|
|||||||
// NoSessionAshTestBase:
|
// NoSessionAshTestBase:
|
||||||
void SetUp() override {
|
void SetUp() override {
|
||||||
scoped_feature_list_.InitWithFeatures(
|
scoped_feature_list_.InitWithFeatures(
|
||||||
{features::kVCBackgroundBlur},
|
{features::kVcControlsUi, features::kVCBackgroundBlur},
|
||||||
{features::kVCBackgroundReplace, features::kVCPortraitRelighting});
|
{features::kVCBackgroundReplace, features::kVCPortraitRelighting});
|
||||||
|
|
||||||
|
// Here we have to create the global instance of `CrasAudioHandler` before
|
||||||
|
// `FakeVideoConferenceTrayController`, so we do it here and not 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();
|
AshTestBase::SetUp();
|
||||||
|
|
||||||
camera_effects_controller_ = Shell::Get()->camera_effects_controller();
|
camera_effects_controller_ = Shell::Get()->camera_effects_controller();
|
||||||
@@ -49,6 +63,13 @@ class CameraEffectsControllerTest : public NoSessionAshTestBase {
|
|||||||
cros::mojom::SetEffectResult::kOk);
|
cros::mojom::SetEffectResult::kOk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
NoSessionAshTestBase::TearDown();
|
||||||
|
controller_.reset();
|
||||||
|
CrasAudioHandler::Shutdown();
|
||||||
|
CrasAudioClient::Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
// Enables/Disables pref values.
|
// Enables/Disables pref values.
|
||||||
void SetEnabledPref(const std::string& perf_name, bool enabled) {
|
void SetEnabledPref(const std::string& perf_name, bool enabled) {
|
||||||
Shell::Get()->session_controller()->GetActivePrefService()->SetBoolean(
|
Shell::Get()->session_controller()->GetActivePrefService()->SetBoolean(
|
||||||
@@ -61,8 +82,21 @@ class CameraEffectsControllerTest : public NoSessionAshTestBase {
|
|||||||
prefs::kBackgroundBlur, level);
|
prefs::kBackgroundBlur, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retrieves the value of `prefs::kBackgroundBlur`.
|
||||||
|
int GetBackgroundBlurPref() {
|
||||||
|
return Shell::Get()
|
||||||
|
->session_controller()
|
||||||
|
->GetActivePrefService()
|
||||||
|
->GetInteger(prefs::kBackgroundBlur);
|
||||||
|
}
|
||||||
|
|
||||||
|
CameraEffectsController* camera_effects_controller() {
|
||||||
|
return camera_effects_controller_;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
CameraEffectsController* camera_effects_controller_ = nullptr;
|
CameraEffectsController* camera_effects_controller_ = nullptr;
|
||||||
|
std::unique_ptr<FakeVideoConferenceTrayController> controller_;
|
||||||
base::test::ScopedFeatureList scoped_feature_list_;
|
base::test::ScopedFeatureList scoped_feature_list_;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -82,12 +116,19 @@ TEST_F(CameraEffectsControllerTest,
|
|||||||
cros::mojom::CameraEffect::kBackgroundReplace));
|
cros::mojom::CameraEffect::kBackgroundReplace));
|
||||||
EXPECT_FALSE(CameraEffectsController::IsCameraEffectsSupported(
|
EXPECT_FALSE(CameraEffectsController::IsCameraEffectsSupported(
|
||||||
cros::mojom::CameraEffect::kPortraitRelight));
|
cros::mojom::CameraEffect::kPortraitRelight));
|
||||||
|
|
||||||
|
// No camera effects supported and VC controls UI not enabled, no camera
|
||||||
|
// effects UI controls available.
|
||||||
|
EXPECT_TRUE(camera_effects_controller());
|
||||||
|
EXPECT_FALSE(camera_effects_controller()->IsEffectControlAvailable());
|
||||||
|
EXPECT_FALSE(camera_effects_controller()->IsEffectControlAvailable(
|
||||||
|
cros::mojom::CameraEffect::kBackgroundBlur));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
base::test::ScopedFeatureList scoped_feature_list;
|
base::test::ScopedFeatureList scoped_feature_list;
|
||||||
scoped_feature_list.InitWithFeatures(
|
scoped_feature_list.InitWithFeatures(
|
||||||
{features::kVCBackgroundBlur},
|
{features::kVcControlsUi, features::kVCBackgroundBlur},
|
||||||
{features::kVCBackgroundReplace, features::kVCPortraitRelighting});
|
{features::kVCBackgroundReplace, features::kVCPortraitRelighting});
|
||||||
|
|
||||||
// BackgroundBlur should be supported.
|
// BackgroundBlur should be supported.
|
||||||
@@ -98,6 +139,13 @@ TEST_F(CameraEffectsControllerTest,
|
|||||||
cros::mojom::CameraEffect::kBackgroundReplace));
|
cros::mojom::CameraEffect::kBackgroundReplace));
|
||||||
EXPECT_FALSE(CameraEffectsController::IsCameraEffectsSupported(
|
EXPECT_FALSE(CameraEffectsController::IsCameraEffectsSupported(
|
||||||
cros::mojom::CameraEffect::kPortraitRelight));
|
cros::mojom::CameraEffect::kPortraitRelight));
|
||||||
|
|
||||||
|
// Camera effects are supported, VC controls UI enabled, so camera effects
|
||||||
|
// UI controls are available, and background blur UI is available.
|
||||||
|
EXPECT_TRUE(camera_effects_controller());
|
||||||
|
EXPECT_TRUE(camera_effects_controller()->IsEffectControlAvailable());
|
||||||
|
EXPECT_TRUE(camera_effects_controller()->IsEffectControlAvailable(
|
||||||
|
cros::mojom::CameraEffect::kBackgroundBlur));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -445,5 +493,90 @@ TEST_F(CameraEffectsControllerTest, NotifyObserverTest) {
|
|||||||
camera_effects_controller_->RemoveObserver(&observer);
|
camera_effects_controller_->RemoveObserver(&observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(CameraEffectsControllerTest, BackgroundBlurGetEffectState) {
|
||||||
|
SimulateUserLogin("testuser@gmail.com");
|
||||||
|
|
||||||
|
// Pref value is `kBackgroundBlurLevelForDisabling` (off).
|
||||||
|
SetBackgroundBlurPref(-1);
|
||||||
|
EXPECT_EQ(camera_effects_controller_->GetEffectState(
|
||||||
|
static_cast<int>(cros::mojom::CameraEffect::kBackgroundBlur)),
|
||||||
|
CameraEffectsController::BackgroundBlurEffectState::kOff);
|
||||||
|
|
||||||
|
// Test all values of `cros::mojom::BlurLevel`.
|
||||||
|
SetBackgroundBlurPref(static_cast<int>(cros::mojom::BlurLevel::kLowest));
|
||||||
|
EXPECT_EQ(camera_effects_controller_->GetEffectState(
|
||||||
|
static_cast<int>(cros::mojom::CameraEffect::kBackgroundBlur)),
|
||||||
|
CameraEffectsController::BackgroundBlurEffectState::kLowest);
|
||||||
|
SetBackgroundBlurPref(static_cast<int>(cros::mojom::BlurLevel::kLight));
|
||||||
|
EXPECT_EQ(camera_effects_controller_->GetEffectState(
|
||||||
|
static_cast<int>(cros::mojom::CameraEffect::kBackgroundBlur)),
|
||||||
|
CameraEffectsController::BackgroundBlurEffectState::kLight);
|
||||||
|
SetBackgroundBlurPref(static_cast<int>(cros::mojom::BlurLevel::kMedium));
|
||||||
|
EXPECT_EQ(camera_effects_controller_->GetEffectState(
|
||||||
|
static_cast<int>(cros::mojom::CameraEffect::kBackgroundBlur)),
|
||||||
|
CameraEffectsController::BackgroundBlurEffectState::kMedium);
|
||||||
|
SetBackgroundBlurPref(static_cast<int>(cros::mojom::BlurLevel::kHeavy));
|
||||||
|
EXPECT_EQ(camera_effects_controller_->GetEffectState(
|
||||||
|
static_cast<int>(cros::mojom::CameraEffect::kBackgroundBlur)),
|
||||||
|
CameraEffectsController::BackgroundBlurEffectState::kHeavy);
|
||||||
|
SetBackgroundBlurPref(static_cast<int>(cros::mojom::BlurLevel::kMaximum));
|
||||||
|
EXPECT_EQ(camera_effects_controller_->GetEffectState(
|
||||||
|
static_cast<int>(cros::mojom::CameraEffect::kBackgroundBlur)),
|
||||||
|
CameraEffectsController::BackgroundBlurEffectState::kMaximum);
|
||||||
|
|
||||||
|
// Now verify with a pref value that isn't recognized as a valid background
|
||||||
|
// blur state.
|
||||||
|
SetBackgroundBlurPref(-999);
|
||||||
|
EXPECT_EQ(camera_effects_controller_->GetEffectState(
|
||||||
|
static_cast<int>(cros::mojom::CameraEffect::kBackgroundBlur)),
|
||||||
|
CameraEffectsController::BackgroundBlurEffectState::kOff);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CameraEffectsControllerTest, BackgroundBlurOnEffectControlActivated) {
|
||||||
|
SimulateUserLogin("testuser@gmail.com");
|
||||||
|
|
||||||
|
// Activate `kOff`, verify that pref value is -1
|
||||||
|
// (`kBackgroundBlurLevelForDisabling`).
|
||||||
|
camera_effects_controller_->OnEffectControlActivated(
|
||||||
|
static_cast<int>(cros::mojom::CameraEffect::kBackgroundBlur),
|
||||||
|
CameraEffectsController::BackgroundBlurEffectState::kOff);
|
||||||
|
EXPECT_EQ(GetBackgroundBlurPref(), -1);
|
||||||
|
|
||||||
|
// Activate the rest of the possible values of
|
||||||
|
// `CameraEffectsController::BackgroundBlurEffectState`, verify that the pref
|
||||||
|
// value is the expected value of `cros::mojom::BlurLevel`.
|
||||||
|
camera_effects_controller_->OnEffectControlActivated(
|
||||||
|
static_cast<int>(cros::mojom::CameraEffect::kBackgroundBlur),
|
||||||
|
CameraEffectsController::BackgroundBlurEffectState::kLowest);
|
||||||
|
EXPECT_EQ(GetBackgroundBlurPref(),
|
||||||
|
static_cast<int>(cros::mojom::BlurLevel::kLowest));
|
||||||
|
camera_effects_controller_->OnEffectControlActivated(
|
||||||
|
static_cast<int>(cros::mojom::CameraEffect::kBackgroundBlur),
|
||||||
|
CameraEffectsController::BackgroundBlurEffectState::kLight);
|
||||||
|
EXPECT_EQ(GetBackgroundBlurPref(),
|
||||||
|
static_cast<int>(cros::mojom::BlurLevel::kLight));
|
||||||
|
camera_effects_controller_->OnEffectControlActivated(
|
||||||
|
static_cast<int>(cros::mojom::CameraEffect::kBackgroundBlur),
|
||||||
|
CameraEffectsController::BackgroundBlurEffectState::kMedium);
|
||||||
|
EXPECT_EQ(GetBackgroundBlurPref(),
|
||||||
|
static_cast<int>(cros::mojom::BlurLevel::kMedium));
|
||||||
|
camera_effects_controller_->OnEffectControlActivated(
|
||||||
|
static_cast<int>(cros::mojom::CameraEffect::kBackgroundBlur),
|
||||||
|
CameraEffectsController::BackgroundBlurEffectState::kHeavy);
|
||||||
|
EXPECT_EQ(GetBackgroundBlurPref(),
|
||||||
|
static_cast<int>(cros::mojom::BlurLevel::kHeavy));
|
||||||
|
camera_effects_controller_->OnEffectControlActivated(
|
||||||
|
static_cast<int>(cros::mojom::CameraEffect::kBackgroundBlur),
|
||||||
|
CameraEffectsController::BackgroundBlurEffectState::kMaximum);
|
||||||
|
EXPECT_EQ(GetBackgroundBlurPref(),
|
||||||
|
static_cast<int>(cros::mojom::BlurLevel::kMaximum));
|
||||||
|
|
||||||
|
// Passing an invalid background blur state is the same as activating
|
||||||
|
// `kOff`.
|
||||||
|
camera_effects_controller_->OnEffectControlActivated(
|
||||||
|
static_cast<int>(cros::mojom::CameraEffect::kBackgroundBlur), -999);
|
||||||
|
EXPECT_EQ(GetBackgroundBlurPref(), -1);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace ash
|
} // namespace ash
|
||||||
|
@@ -22,6 +22,10 @@ enum BubbleViewID {
|
|||||||
// `kMainBubbleView`.
|
// `kMainBubbleView`.
|
||||||
kSetValueEffectsView,
|
kSetValueEffectsView,
|
||||||
|
|
||||||
|
// Container view for a single "set-value" VC effect, a child of
|
||||||
|
// `kSetValueEffectsView`.
|
||||||
|
kSingleSetValueEffectView,
|
||||||
|
|
||||||
// Container view for all "toggle" VC effects, a child of `kMainBubbleView`.
|
// Container view for all "toggle" VC effects, a child of `kMainBubbleView`.
|
||||||
kToggleEffectsView,
|
kToggleEffectsView,
|
||||||
|
|
||||||
|
@@ -47,6 +47,8 @@ class BubbleViewTest : public AshTestBase {
|
|||||||
office_bunny_ =
|
office_bunny_ =
|
||||||
std::make_unique<fake_video_conference::OfficeBunnyEffect>();
|
std::make_unique<fake_video_conference::OfficeBunnyEffect>();
|
||||||
shaggy_fur_ = std::make_unique<fake_video_conference::ShaggyFurEffect>();
|
shaggy_fur_ = std::make_unique<fake_video_conference::ShaggyFurEffect>();
|
||||||
|
super_cuteness_ =
|
||||||
|
std::make_unique<fake_video_conference::SuperCutnessEffect>();
|
||||||
|
|
||||||
set_create_global_cras_audio_handler(false);
|
set_create_global_cras_audio_handler(false);
|
||||||
AshTestBase::SetUp();
|
AshTestBase::SetUp();
|
||||||
@@ -59,6 +61,7 @@ class BubbleViewTest : public AshTestBase {
|
|||||||
AshTestBase::TearDown();
|
AshTestBase::TearDown();
|
||||||
office_bunny_.reset();
|
office_bunny_.reset();
|
||||||
shaggy_fur_.reset();
|
shaggy_fur_.reset();
|
||||||
|
super_cuteness_.reset();
|
||||||
controller_.reset();
|
controller_.reset();
|
||||||
CrasAudioHandler::Shutdown();
|
CrasAudioHandler::Shutdown();
|
||||||
CrasAudioClient::Shutdown();
|
CrasAudioClient::Shutdown();
|
||||||
@@ -98,6 +101,11 @@ class BubbleViewTest : public AshTestBase {
|
|||||||
video_conference::BubbleViewID::kSetValueEffectsView);
|
video_conference::BubbleViewID::kSetValueEffectsView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
views::View* single_set_value_effect_view() {
|
||||||
|
return bubble_view()->GetViewByID(
|
||||||
|
video_conference::BubbleViewID::kSingleSetValueEffectView);
|
||||||
|
}
|
||||||
|
|
||||||
views::View* return_to_app() {
|
views::View* return_to_app() {
|
||||||
return bubble_view()->GetViewByID(
|
return bubble_view()->GetViewByID(
|
||||||
video_conference::BubbleViewID::kReturnToApp);
|
video_conference::BubbleViewID::kReturnToApp);
|
||||||
@@ -116,11 +124,17 @@ class BubbleViewTest : public AshTestBase {
|
|||||||
return shaggy_fur_.get();
|
return shaggy_fur_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ash::fake_video_conference::SuperCutnessEffect* super_cuteness() {
|
||||||
|
return super_cuteness_.get();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
base::test::ScopedFeatureList scoped_feature_list_;
|
base::test::ScopedFeatureList scoped_feature_list_;
|
||||||
std::unique_ptr<FakeVideoConferenceTrayController> controller_;
|
std::unique_ptr<FakeVideoConferenceTrayController> controller_;
|
||||||
std::unique_ptr<ash::fake_video_conference::OfficeBunnyEffect> office_bunny_;
|
std::unique_ptr<ash::fake_video_conference::OfficeBunnyEffect> office_bunny_;
|
||||||
std::unique_ptr<ash::fake_video_conference::ShaggyFurEffect> shaggy_fur_;
|
std::unique_ptr<ash::fake_video_conference::ShaggyFurEffect> shaggy_fur_;
|
||||||
|
std::unique_ptr<ash::fake_video_conference::SuperCutnessEffect>
|
||||||
|
super_cuteness_;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(BubbleViewTest, NoEffects) {
|
TEST_F(BubbleViewTest, NoEffects) {
|
||||||
@@ -274,4 +288,42 @@ TEST_F(BubbleViewTest, SetValueButtonClicked) {
|
|||||||
EXPECT_EQ(shaggy_fur()->GetNumActivationsForTesting(1), 1);
|
EXPECT_EQ(shaggy_fur()->GetNumActivationsForTesting(1), 1);
|
||||||
EXPECT_EQ(shaggy_fur()->GetNumActivationsForTesting(0), 1);
|
EXPECT_EQ(shaggy_fur()->GetNumActivationsForTesting(0), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(BubbleViewTest, ValidEffectState) {
|
||||||
|
// Verify that the delegate hosts a single effect which has at least two
|
||||||
|
// values.
|
||||||
|
EXPECT_EQ(super_cuteness()->GetNumEffects(), 1);
|
||||||
|
EXPECT_GE(super_cuteness()->GetEffect(0)->GetNumStates(), 2);
|
||||||
|
|
||||||
|
// Add one set-value effect.
|
||||||
|
controller()->effects_manager().RegisterDelegate(super_cuteness());
|
||||||
|
|
||||||
|
// Effect will NOT return an invalid state.
|
||||||
|
super_cuteness()->set_has_invalid_effect_state_for_testing(false);
|
||||||
|
|
||||||
|
// Click to open the bubble, a single set-value effect view is
|
||||||
|
// present/visible.
|
||||||
|
LeftClickOn(toggle_bubble_button());
|
||||||
|
views::View* effect_view = single_set_value_effect_view();
|
||||||
|
EXPECT_TRUE(effect_view);
|
||||||
|
EXPECT_TRUE(effect_view->GetVisible());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(BubbleViewTest, InvalidEffectState) {
|
||||||
|
// Verify that the delegate hosts a single effect which has at least two
|
||||||
|
// values.
|
||||||
|
EXPECT_EQ(super_cuteness()->GetNumEffects(), 1);
|
||||||
|
EXPECT_GE(super_cuteness()->GetEffect(0)->GetNumStates(), 2);
|
||||||
|
|
||||||
|
// Add one set-value effect.
|
||||||
|
controller()->effects_manager().RegisterDelegate(super_cuteness());
|
||||||
|
|
||||||
|
// Effect WILL return an invalid state.
|
||||||
|
super_cuteness()->set_has_invalid_effect_state_for_testing(true);
|
||||||
|
|
||||||
|
// Click to open the bubble, a single set-value effect view is NOT present.
|
||||||
|
LeftClickOn(toggle_bubble_button());
|
||||||
|
EXPECT_FALSE(single_set_value_effect_view());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ash::video_conference
|
} // namespace ash::video_conference
|
@@ -24,6 +24,7 @@ namespace {
|
|||||||
class ValueButtonContainer : public views::View {
|
class ValueButtonContainer : public views::View {
|
||||||
public:
|
public:
|
||||||
explicit ValueButtonContainer(const VcHostedEffect* effect) {
|
explicit ValueButtonContainer(const VcHostedEffect* effect) {
|
||||||
|
SetID(BubbleViewID::kSingleSetValueEffectView);
|
||||||
views::FlexLayout* layout =
|
views::FlexLayout* layout =
|
||||||
SetLayoutManager(std::make_unique<views::FlexLayout>());
|
SetLayoutManager(std::make_unique<views::FlexLayout>());
|
||||||
layout->SetOrientation(views::LayoutOrientation::kVertical);
|
layout->SetOrientation(views::LayoutOrientation::kVertical);
|
||||||
@@ -34,8 +35,13 @@ class ValueButtonContainer : public views::View {
|
|||||||
AddChildView(std::make_unique<views::Label>(effect->label_text()));
|
AddChildView(std::make_unique<views::Label>(effect->label_text()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a button for each state.
|
// `effect` is expected to provide the current state of the effect, and
|
||||||
|
// a `current_state` of `VcEffectState::kUnusedId` means it couldn't be
|
||||||
|
// obtained.
|
||||||
const int current_state = effect->get_state_callback().Run();
|
const int current_state = effect->get_state_callback().Run();
|
||||||
|
DCHECK(current_state != VcEffectState::kUnusedId);
|
||||||
|
|
||||||
|
// Add a button for each state.
|
||||||
for (int i = 0; i < effect->GetNumStates(); ++i) {
|
for (int i = 0; i < effect->GetNumStates(); ++i) {
|
||||||
const VcEffectState* state = effect->GetState(/*index=*/i);
|
const VcEffectState* state = effect->GetState(/*index=*/i);
|
||||||
std::unique_ptr<views::RadioButton> state_button =
|
std::unique_ptr<views::RadioButton> state_button =
|
||||||
@@ -83,6 +89,15 @@ SetValueEffectsView::SetValueEffectsView(
|
|||||||
|
|
||||||
if (controller->effects_manager().HasSetValueEffects()) {
|
if (controller->effects_manager().HasSetValueEffects()) {
|
||||||
for (auto* effect : controller->effects_manager().GetSetValueEffects()) {
|
for (auto* effect : controller->effects_manager().GetSetValueEffects()) {
|
||||||
|
// Make sure the current value of the effect can be obtained, and if it
|
||||||
|
// can't then don't present its controls.
|
||||||
|
if (effect->get_state_callback().Run() == VcEffectState::kUnusedId) {
|
||||||
|
LOG(ERROR) << __FUNCTION__ << " effect with id (" << effect->id()
|
||||||
|
<< ") label_text (" << effect->label_text()
|
||||||
|
<< ") could not obtain its current value";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
AddChildView(std::make_unique<ValueButtonContainer>(effect));
|
AddChildView(std::make_unique<ValueButtonContainer>(effect));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -170,6 +170,10 @@ SuperCutnessEffect::SuperCutnessEffect() {
|
|||||||
SuperCutnessEffect::~SuperCutnessEffect() = default;
|
SuperCutnessEffect::~SuperCutnessEffect() = default;
|
||||||
|
|
||||||
int SuperCutnessEffect::GetEffectState(int effect_id) {
|
int SuperCutnessEffect::GetEffectState(int effect_id) {
|
||||||
|
if (has_invalid_effect_state_for_testing_) {
|
||||||
|
return VcEffectState::kUnusedId;
|
||||||
|
}
|
||||||
|
|
||||||
return static_cast<int>(HowCute::kTeddyBear);
|
return static_cast<int>(HowCute::kTeddyBear);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -189,6 +189,13 @@ class ASH_EXPORT SuperCutnessEffect : public VcEffectsDelegate {
|
|||||||
// activated.
|
// activated.
|
||||||
int GetNumActivationsForTesting(int value);
|
int GetNumActivationsForTesting(int value);
|
||||||
|
|
||||||
|
void set_has_invalid_effect_state_for_testing(bool has_invalid_state) {
|
||||||
|
has_invalid_effect_state_for_testing_ = has_invalid_state;
|
||||||
|
}
|
||||||
|
bool has_invalid_effect_state_for_testing() {
|
||||||
|
return has_invalid_effect_state_for_testing_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Adds a `std::unique_ptr<VcEffectState>` to `effect`.
|
// Adds a `std::unique_ptr<VcEffectState>` to `effect`.
|
||||||
void AddStateToEffect(VcHostedEffect* effect,
|
void AddStateToEffect(VcHostedEffect* effect,
|
||||||
@@ -199,6 +206,10 @@ class ASH_EXPORT SuperCutnessEffect : public VcEffectsDelegate {
|
|||||||
// `HowCute`.
|
// `HowCute`.
|
||||||
std::vector<int> num_activations_for_testing_;
|
std::vector<int> num_activations_for_testing_;
|
||||||
|
|
||||||
|
// Set to 'true' for testing the case where a valid effect state cannot be
|
||||||
|
// obtained.
|
||||||
|
bool has_invalid_effect_state_for_testing_;
|
||||||
|
|
||||||
base::WeakPtrFactory<SuperCutnessEffect> weak_factory_{this};
|
base::WeakPtrFactory<SuperCutnessEffect> weak_factory_{this};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -47,7 +47,9 @@ class ASH_EXPORT VcEffectsDelegate {
|
|||||||
|
|
||||||
// Invoked when the UI controls are being constructed, to get the actual
|
// Invoked when the UI controls are being constructed, to get the actual
|
||||||
// effect state. `effect_id` specifies the effect whose state is requested,
|
// effect state. `effect_id` specifies the effect whose state is requested,
|
||||||
// and can be ignored if only one effect is being hosted.
|
// and can be ignored if only one effect is being hosted. If no state can be
|
||||||
|
// determined for `effect_id`, this function should return
|
||||||
|
// `VcEffectState::kUnusedId`.
|
||||||
virtual int GetEffectState(int effect_id) = 0;
|
virtual int GetEffectState(int effect_id) = 0;
|
||||||
|
|
||||||
// Invoked anytime the user makes an adjustment. `effect_id` is the unique ID
|
// Invoked anytime the user makes an adjustment. `effect_id` is the unique ID
|
||||||
|
@@ -21,10 +21,7 @@ VideoConferenceTrayEffectsManager::~VideoConferenceTrayEffectsManager() =
|
|||||||
void VideoConferenceTrayEffectsManager::RegisterDelegate(
|
void VideoConferenceTrayEffectsManager::RegisterDelegate(
|
||||||
VcEffectsDelegate* delegate) {
|
VcEffectsDelegate* delegate) {
|
||||||
DCHECK(delegate);
|
DCHECK(delegate);
|
||||||
DCHECK(std::find_if(effect_delegates_.begin(), effect_delegates_.end(),
|
DCHECK(!IsDelegateRegistered(delegate));
|
||||||
[delegate](VcEffectsDelegate* d) {
|
|
||||||
return delegate == d;
|
|
||||||
}) == effect_delegates_.end());
|
|
||||||
effect_delegates_.push_back(delegate);
|
effect_delegates_.push_back(delegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,6 +34,15 @@ void VideoConferenceTrayEffectsManager::UnregisterDelegate(
|
|||||||
DCHECK_EQ(num_items_erased, 1UL);
|
DCHECK_EQ(num_items_erased, 1UL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VideoConferenceTrayEffectsManager::IsDelegateRegistered(
|
||||||
|
VcEffectsDelegate* delegate) {
|
||||||
|
DCHECK(delegate);
|
||||||
|
return std::find_if(effect_delegates_.begin(), effect_delegates_.end(),
|
||||||
|
[delegate](VcEffectsDelegate* d) {
|
||||||
|
return delegate == d;
|
||||||
|
}) != effect_delegates_.end();
|
||||||
|
}
|
||||||
|
|
||||||
bool VideoConferenceTrayEffectsManager::HasToggleEffects() {
|
bool VideoConferenceTrayEffectsManager::HasToggleEffects() {
|
||||||
return GetTotalToggleEffectButtons().size() > 0;
|
return GetTotalToggleEffectButtons().size() > 0;
|
||||||
}
|
}
|
||||||
@@ -49,8 +55,9 @@ VideoConferenceTrayEffectsManager::GetToggleEffectButtonTable() {
|
|||||||
EffectDataTable buttons;
|
EffectDataTable buttons;
|
||||||
|
|
||||||
int num_buttons = total_buttons.size();
|
int num_buttons = total_buttons.size();
|
||||||
if (num_buttons == 0)
|
if (num_buttons == 0) {
|
||||||
return buttons;
|
return buttons;
|
||||||
|
}
|
||||||
|
|
||||||
if (num_buttons <= 3) {
|
if (num_buttons <= 3) {
|
||||||
// For 3 or fewer, `effects_buttons` is the entire row.
|
// For 3 or fewer, `effects_buttons` is the entire row.
|
||||||
@@ -80,8 +87,9 @@ VideoConferenceTrayEffectsManager::GetSetValueEffects() {
|
|||||||
EffectDataVector effects;
|
EffectDataVector effects;
|
||||||
|
|
||||||
for (auto* delegate : effect_delegates_) {
|
for (auto* delegate : effect_delegates_) {
|
||||||
for (auto* effect : delegate->GetEffects(VcEffectType::kSetValue))
|
for (auto* effect : delegate->GetEffects(VcEffectType::kSetValue)) {
|
||||||
effects.push_back(effect);
|
effects.push_back(effect);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return effects;
|
return effects;
|
||||||
@@ -92,8 +100,9 @@ VideoConferenceTrayEffectsManager::GetTotalToggleEffectButtons() {
|
|||||||
EffectDataVector effects;
|
EffectDataVector effects;
|
||||||
|
|
||||||
for (auto* delegate : effect_delegates_) {
|
for (auto* delegate : effect_delegates_) {
|
||||||
for (auto* effect : delegate->GetEffects(VcEffectType::kToggle))
|
for (auto* effect : delegate->GetEffects(VcEffectType::kToggle)) {
|
||||||
effects.push_back(effect);
|
effects.push_back(effect);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return effects;
|
return effects;
|
||||||
|
@@ -32,6 +32,9 @@ class ASH_EXPORT VideoConferenceTrayEffectsManager {
|
|||||||
void RegisterDelegate(VcEffectsDelegate* delegate);
|
void RegisterDelegate(VcEffectsDelegate* delegate);
|
||||||
void UnregisterDelegate(VcEffectsDelegate* delegate);
|
void UnregisterDelegate(VcEffectsDelegate* delegate);
|
||||||
|
|
||||||
|
// Returns 'true' if `delegate` is registered, 'false' otherwise.
|
||||||
|
bool IsDelegateRegistered(VcEffectsDelegate* delegate);
|
||||||
|
|
||||||
// A vector (or row) of `VcHostedEffect` objects of type
|
// A vector (or row) of `VcHostedEffect` objects of type
|
||||||
// `VcEffectType::kToggle`.
|
// `VcEffectType::kToggle`.
|
||||||
using EffectDataVector = std::vector<const VcHostedEffect*>;
|
using EffectDataVector = std::vector<const VcHostedEffect*>;
|
||||||
|
Reference in New Issue
Block a user