blob: e8748f11a05e53edad739c7514520ce380adc42f [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/system/audio/unified_volume_view.h"
#include "ash/public/cpp/ash_features.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/style/default_color_constants.h"
#include "ash/system/audio/unified_volume_slider_controller.h"
#include "ash/system/tray/tray_popup_utils.h"
#include "ash/system/unified/unified_system_tray_view.h"
#include "base/i18n/rtl.h"
#include "base/stl_util.h"
#include "components/vector_icons/vector_icons.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/animation/flood_fill_ink_drop_ripple.h"
#include "ui/views/animation/ink_drop_highlight.h"
#include "ui/views/animation/ink_drop_impl.h"
#include "ui/views/animation/ink_drop_mask.h"
#include "ui/views/controls/highlight_path_generator.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/layout/box_layout.h"
using chromeos::CrasAudioHandler;
namespace ash {
namespace {
// References to the icons that correspond to different volume levels.
const gfx::VectorIcon* const kVolumeLevelIcons[] = {
&kUnifiedMenuVolumeLowIcon, // Low volume.
&kUnifiedMenuVolumeMediumIcon, // Medium volume.
&kUnifiedMenuVolumeHighIcon, // High volume.
&kUnifiedMenuVolumeHighIcon, // Full volume.
};
// The maximum index of kVolumeLevelIcons.
constexpr int kVolumeLevels = base::size(kVolumeLevelIcons) - 1;
// Get vector icon reference that corresponds to the given volume level. |level|
// is between 0.0 to 1.0.
const gfx::VectorIcon& GetVolumeIconForLevel(float level) {
int index = static_cast<int>(std::ceil(level * kVolumeLevels));
if (index < 0)
index = 0;
else if (index > kVolumeLevels)
index = kVolumeLevels;
return *kVolumeLevelIcons[index];
}
class MoreButton : public views::Button {
public:
explicit MoreButton(views::ButtonListener* listener)
: views::Button(listener) {
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal,
gfx::Insets((kTrayItemSize -
GetDefaultSizeOfVectorIcon(vector_icons::kHeadsetIcon)) /
2),
2));
const SkColor icon_color = AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kIconPrimary,
AshColorProvider::AshColorMode::kDark);
if (!features::IsSystemTrayMicGainSettingEnabled()) {
auto* headset = new views::ImageView();
headset->set_can_process_events_within_subtree(false);
headset->SetImage(
CreateVectorIcon(vector_icons::kHeadsetIcon, icon_color));
AddChildView(headset);
}
auto* more = new views::ImageView();
more->set_can_process_events_within_subtree(false);
auto icon_rotation = base::i18n::IsRTL()
? SkBitmapOperations::ROTATION_270_CW
: SkBitmapOperations::ROTATION_90_CW;
more->SetImage(gfx::ImageSkiaOperations::CreateRotatedImage(
CreateVectorIcon(kUnifiedMenuExpandIcon, icon_color), icon_rotation));
AddChildView(more);
SetTooltipText(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO));
TrayPopupUtils::ConfigureTrayPopupButton(this);
views::InstallRoundRectHighlightPathGenerator(this, gfx::Insets(),
kTrayItemCornerRadius);
focus_ring()->SetColor(UnifiedSystemTrayView::GetFocusRingColor());
}
~MoreButton() override = default;
// views::Button:
void PaintButtonContents(gfx::Canvas* canvas) override {
gfx::RectF rect(GetContentsBounds());
cc::PaintFlags flags;
flags.setAntiAlias(true);
flags.setColor(AshColorProvider::Get()->DeprecatedGetControlsLayerColor(
AshColorProvider::ControlsLayerType::kInactiveControlBackground,
kUnifiedMenuButtonColor));
flags.setStyle(cc::PaintFlags::kFill_Style);
canvas->DrawRoundRect(rect, kTrayItemCornerRadius, flags);
}
std::unique_ptr<views::InkDrop> CreateInkDrop() override {
return TrayPopupUtils::CreateInkDrop(this);
}
std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override {
return TrayPopupUtils::CreateInkDropRipple(
TrayPopupInkDropStyle::FILL_BOUNDS, this,
GetInkDropCenterBasedOnLastEvent(),
UnifiedSystemTrayView::GetBackgroundColor());
}
std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight()
const override {
return TrayPopupUtils::CreateInkDropHighlight(
TrayPopupInkDropStyle::FILL_BOUNDS, this,
UnifiedSystemTrayView::GetBackgroundColor());
}
const char* GetClassName() const override { return "MoreButton"; }
private:
DISALLOW_COPY_AND_ASSIGN(MoreButton);
};
} // namespace
UnifiedVolumeView::UnifiedVolumeView(UnifiedVolumeSliderController* controller)
: UnifiedSliderView(controller,
kSystemMenuVolumeHighIcon,
IDS_ASH_STATUS_TRAY_VOLUME_SLIDER_LABEL),
more_button_(new MoreButton(controller)) {
CrasAudioHandler::Get()->AddAudioObserver(this);
AddChildView(more_button_);
Update(false /* by_user */);
}
UnifiedVolumeView::~UnifiedVolumeView() {
CrasAudioHandler::Get()->RemoveAudioObserver(this);
}
const char* UnifiedVolumeView::GetClassName() const {
return "UnifiedVolumeView";
}
void UnifiedVolumeView::Update(bool by_user) {
bool is_muted = CrasAudioHandler::Get()->IsOutputMuted();
float level = CrasAudioHandler::Get()->GetOutputVolumePercent() / 100.f;
// To indicate that the volume is muted, set the volume slider to the minimal
// visual style.
slider()->SetRenderingStyle(
is_muted ? views::Slider::RenderingStyle::kMinimalStyle
: views::Slider::RenderingStyle::kDefaultStyle);
// The button should be gray when muted and colored otherwise.
button()->SetToggled(!is_muted);
button()->SetVectorIcon(is_muted ? kUnifiedMenuVolumeMuteIcon
: GetVolumeIconForLevel(level));
base::string16 state_tooltip_text = l10n_util::GetStringUTF16(
is_muted ? IDS_ASH_STATUS_TRAY_VOLUME_STATE_MUTED
: IDS_ASH_STATUS_TRAY_VOLUME_STATE_ON);
button()->SetTooltipText(l10n_util::GetStringFUTF16(
IDS_ASH_STATUS_TRAY_VOLUME, state_tooltip_text));
more_button_->SetVisible(CrasAudioHandler::Get()->has_alternative_input() ||
CrasAudioHandler::Get()->has_alternative_output() ||
features::IsSystemTrayMicGainSettingEnabled());
// Slider's value is in finer granularity than audio volume level(0.01),
// there will be a small discrepancy between slider's value and volume level
// on audio side. To avoid the jittering in slider UI, use the slider's
// current value.
if (std::abs(level - slider()->GetValue()) <
kAudioSliderIgnoreUpdateThreshold) {
level = slider()->GetValue();
}
// Note: even if the value does not change, we still need to call this
// function to enable accessibility events (crbug.com/1013251).
SetSliderValue(level, by_user);
}
void UnifiedVolumeView::OnOutputNodeVolumeChanged(uint64_t node_id,
int volume) {
Update(true /* by_user */);
}
void UnifiedVolumeView::OnOutputMuteChanged(bool mute_on) {
Update(true /* by_user */);
}
void UnifiedVolumeView::OnAudioNodesChanged() {
Update(true /* by_user */);
}
void UnifiedVolumeView::OnActiveOutputNodeChanged() {
Update(true /* by_user */);
}
void UnifiedVolumeView::OnActiveInputNodeChanged() {
Update(true /* by_user */);
}
void UnifiedVolumeView::ChildVisibilityChanged(views::View* child) {
Layout();
}
} // namespace ash