#include "ash/system/audio/labeled_slider_view.h"
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/typography.h"
#include "ash/system/audio/audio_detailed_view_utils.h"
#include "ash/system/tray/hover_highlight_view.h"
#include "ash/system/tray/tray_detailed_view.h"
#include "ash/system/tray/tray_popup_utils.h"
#include "ash/system/unified/unified_slider_view.h"
#include "base/strings/utf_string_conversions.h"
#include "chromeos/ash/components/audio/audio_device.h"
#include "chromeos/ash/components/audio/cras_audio_handler.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/geometry/rounded_corners_f.h"
#include "ui/gfx/geometry/rrect_f.h"
#include "ui/gfx/vector_icon_types.h"
#include "ui/views/controls/focus_ring.h"
#include "ui/views/controls/highlight_path_generator.h"
#include "ui/views/controls/label.h"
#include "ui/views/view_utils.h"
namespace ash {
namespace {
constexpr auto kDevicesNameViewPreferredSize = gfx::Size(0, 44);
constexpr auto kDevicesTriViewInsets = gfx::Insets::TLBR(0, 24, 0, 32);
constexpr auto kDevicesTriViewBorder = gfx::Insets::VH(0, 4);
constexpr auto kWideDevicesSliderBorder = gfx::Insets::VH(4, 8);
constexpr auto kWideDevicesTriViewInsets = gfx::Insets::TLBR(0, 8, 0, 16);
std::u16string GetAudioDeviceName(const AudioDevice& device) {
switch (device.type) {
case AudioDeviceType::kBluetooth:
case AudioDeviceType::kBluetoothNbMic:
return l10n_util::GetStringFUTF16(
IDS_ASH_STATUS_TRAY_AUDIO_BLUETOOTH_DEVICE,
base::UTF8ToUTF16(device.display_name));
case AudioDeviceType::kFrontMic:
return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_FRONT_MIC);
case AudioDeviceType::kHeadphone:
return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_HEADPHONE);
case AudioDeviceType::kHdmi:
return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_AUDIO_HDMI_DEVICE,
base::UTF8ToUTF16(device.display_name));
case AudioDeviceType::kInternalMic:
return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_INTERNAL_MIC);
case AudioDeviceType::kInternalSpeaker:
return l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_AUDIO_INTERNAL_SPEAKER);
case AudioDeviceType::kMic:
return l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_AUDIO_MIC_JACK_DEVICE);
case AudioDeviceType::kRearMic:
return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_REAR_MIC);
case AudioDeviceType::kUsb:
return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_AUDIO_USB_DEVICE,
base::UTF8ToUTF16(device.display_name));
case AudioDeviceType::kAlsaLoopback:
case AudioDeviceType::kHotword:
case AudioDeviceType::kKeyboardMic:
case AudioDeviceType::kLineout:
case AudioDeviceType::kPostDspLoopback:
case AudioDeviceType::kPostMixLoopback:
case AudioDeviceType::kOther:
return base::UTF8ToUTF16(device.display_name);
}
}
class DeviceNameContainerHighlightPathGenerator
: public views::HighlightPathGenerator {
public:
DeviceNameContainerHighlightPathGenerator(QuickSettingsSlider* slider,
bool is_wide_slider)
: slider_(slider), is_wide_slider_(is_wide_slider) {
CHECK(slider);
}
DeviceNameContainerHighlightPathGenerator(
const DeviceNameContainerHighlightPathGenerator&) = delete;
DeviceNameContainerHighlightPathGenerator& operator=(
const DeviceNameContainerHighlightPathGenerator&) = delete;
~DeviceNameContainerHighlightPathGenerator() override = default;
private:
std::optional<gfx::RRectF> GetRoundRect(const gfx::RectF& rect) override {
const gfx::Rect slider_bounds = slider_->GetInactiveRadioSliderRect();
const gfx::RectF bounds(
slider_bounds.x() +
(is_wide_slider_ ? 0 : kRadioSliderViewPadding.left()),
slider_bounds.y(), slider_bounds.width(), slider_bounds.height());
const gfx::RoundedCornersF rounded(
slider_->GetInactiveRadioSliderRoundedCornerRadius());
return gfx::RRectF(bounds, rounded);
}
const raw_ptr<QuickSettingsSlider> slider_;
const bool is_wide_slider_;
};
}
LabeledSliderView::LabeledSliderView(TrayDetailedView* detailed_view,
std::unique_ptr<views::View> slider_view,
const AudioDevice& device,
bool is_wide_slider)
: is_wide_slider_(is_wide_slider) {
SetUseDefaultFillLayout(true);
unified_slider_view_ = views::AsViewClass<UnifiedSliderView>(
AddChildView(std::move(slider_view)));
auto* slider =
views::AsViewClass<QuickSettingsSlider>(unified_slider_view_->slider());
if (is_wide_slider_) {
slider->SetBorder(views::CreateEmptyBorder(kWideDevicesSliderBorder));
}
device_name_view_ = detailed_view->AddScrollListCheckableItem(
this, gfx::VectorIcon::EmptyIcon(), GetAudioDeviceName(device),
device.active);
ConfigureDeviceNameView(device);
device_name_view_->AddLayerToRegion(unified_slider_view_->layer(),
views::LayerRegion::kBelow);
ConfigureFocusBehavior(device.active, slider);
}
LabeledSliderView::~LabeledSliderView() {
views::FocusRing::Remove(device_name_view_);
}
void LabeledSliderView::ConfigureDeviceNameView(const AudioDevice& device) {
device_name_view_->text_label()->SetAutoColorReadabilityEnabled(
false);
device_name_view_->SetPreferredSize(kDevicesNameViewPreferredSize);
device_name_view_->tri_view()->SetInsets(
is_wide_slider_ ? kWideDevicesTriViewInsets : kDevicesTriViewInsets);
device_name_view_->tri_view()->SetContainerBorder(
TriView::Container::CENTER,
views::CreateEmptyBorder(kDevicesTriViewBorder));
const bool is_muted =
device.is_input
? CrasAudioHandler::Get()->IsInputMutedForDevice(device.id)
: CrasAudioHandler::Get()->IsOutputMutedForDevice(device.id);
UpdateDeviceContainerColor(device_name_view_, is_muted, device.active);
TypographyProvider::Get()->StyleLabel(TypographyToken::kCrosButton2,
*device_name_view_->text_label());
}
void LabeledSliderView::ConfigureFocusBehavior(const bool is_active,
QuickSettingsSlider* slider) {
if (is_active) {
device_name_view_->SetFocusBehavior(
HoverHighlightView::FocusBehavior::NEVER);
device_name_view_->SetCanProcessEventsWithinSubtree(false);
} else {
device_name_view_->SetInstallFocusRingOnFocus(true);
views::FocusRing::Get(device_name_view_)
->SetPathGenerator(
std::make_unique<DeviceNameContainerHighlightPathGenerator>(
slider, is_wide_slider_));
device_name_view_->SetFocusPainter(nullptr);
views::FocusRing::Get(device_name_view_)
->SetColorId(cros_tokens::kCrosSysPrimary);
}
}
BEGIN_METADATA(LabeledSliderView)
END_METADATA
}