Commit 63667eca authored by Tetsui Ohkubo's avatar Tetsui Ohkubo Committed by Commit Bot

Add volume slider to UnifiedSystemTray.

This CL implements a volume slider to UnifiedSystemTray.

This CL adds following classes:
* UnifiedVolumeView
  * View class of a volume slider that inherits UnifiedSliderView.
    It observes CrasAudioHandler for current volume and mute state.
    User can toggle mute state by pressing the button.
* UnifiedVolumeSliderController
  * Controller class of the volume slider.
* UnifiedSliderButton
  * Added to support toggling. Inherits TopShortcutButton.

Screenshot: http://screen/AAhfnLdBdHH

UnifiedSystemTray design doc: go/cros-qs-restyling

TEST=manual
BUG=819943

Change-Id: I1af89386f3e5865491f052bbb323f4431c26880a
Reviewed-on: https://chromium-review.googlesource.com/958886Reviewed-by: default avatarYoshiki Iguchi <yoshiki@chromium.org>
Commit-Queue: Tetsui Ohkubo <tetsui@chromium.org>
Cr-Commit-Position: refs/heads/master@{#543290}
parent ea38ede3
......@@ -485,6 +485,10 @@ component("ash") {
"system/audio/audio_detailed_view.h",
"system/audio/tray_audio.cc",
"system/audio/tray_audio.h",
"system/audio/unified_volume_slider_controller.cc",
"system/audio/unified_volume_slider_controller.h",
"system/audio/unified_volume_view.cc",
"system/audio/unified_volume_view.h",
"system/audio/volume_view.cc",
"system/audio/volume_view.h",
"system/bluetooth/bluetooth_feature_pod_controller.cc",
......
......@@ -104,6 +104,11 @@ void TrayAudio::OnOutputNodeVolumeChanged(uint64_t /* node_id */,
SetDetailedViewCloseDelay(kTrayPopupAutoCloseDelayInSeconds);
return;
}
// Show popup only when UnifiedSystemTray bubble is not shown.
if (IsUnifiedBubbleShown())
return;
pop_up_volume_view_ = true;
ShowDetailedView(kTrayPopupAutoCloseDelayInSeconds);
}
......@@ -116,6 +121,10 @@ void TrayAudio::OnOutputMuteChanged(bool /* mute_on */, bool system_adjust) {
volume_view_->Update();
SetDetailedViewCloseDelay(kTrayPopupAutoCloseDelayInSeconds);
} else if (!system_adjust) {
// Show popup only when UnifiedSystemTray bubble is not shown.
if (IsUnifiedBubbleShown())
return;
pop_up_volume_view_ = true;
ShowDetailedView(kTrayPopupAutoCloseDelayInSeconds);
}
......
// 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_slider_controller.h"
#include "ash/system/audio/unified_volume_view.h"
using chromeos::CrasAudioHandler;
namespace ash {
UnifiedVolumeSliderController::UnifiedVolumeSliderController() = default;
UnifiedVolumeSliderController::~UnifiedVolumeSliderController() = default;
views::View* UnifiedVolumeSliderController::CreateView() {
DCHECK(!slider_);
slider_ = new UnifiedVolumeView(this);
return slider_;
}
void UnifiedVolumeSliderController::ButtonPressed(views::Button* sender,
const ui::Event& event) {
CrasAudioHandler::Get()->SetOutputMute(
!CrasAudioHandler::Get()->IsOutputMuted());
}
void UnifiedVolumeSliderController::SliderValueChanged(
views::Slider* sender,
float value,
float old_value,
views::SliderChangeReason reason) {
if (reason != views::VALUE_CHANGED_BY_USER)
return;
const int level = value * 100;
CrasAudioHandler::Get()->SetOutputVolumePercent(level);
// If the volume is above certain level and it's muted, it should be unmuted.
// If the volume is below certain level and it's unmuted, it should be muted.
if (CrasAudioHandler::Get()->IsOutputMuted() ==
level > CrasAudioHandler::Get()->GetOutputDefaultVolumeMuteThreshold()) {
CrasAudioHandler::Get()->SetOutputMute(
!CrasAudioHandler::Get()->IsOutputMuted());
}
}
} // namespace ash
// 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.
#ifndef ASH_SYSTEM_AUDIO_UNIFIED_VOLUME_SLIDER_CONTROLLER_H_
#define ASH_SYSTEM_AUDIO_UNIFIED_VOLUME_SLIDER_CONTROLLER_H_
#include "ash/system/unified/unified_slider_view.h"
namespace ash {
// Controller of a slider that can change audio volume.
class UnifiedVolumeSliderController : public UnifiedSliderListener {
public:
UnifiedVolumeSliderController();
~UnifiedVolumeSliderController() override;
// Instantiates UnifiedSliderView. The view will be onwed by views hierarchy.
// The view should be always deleted after the controller is destructed.
views::View* CreateView();
// UnifiedSliderListener:
void ButtonPressed(views::Button* sender, const ui::Event& event) override;
void SliderValueChanged(views::Slider* sender,
float value,
float old_value,
views::SliderChangeReason reason) override;
private:
UnifiedSliderView* slider_;
DISALLOW_COPY_AND_ASSIGN(UnifiedVolumeSliderController);
};
} // namespace ash
#endif // ASH_SYSTEM_AUDIO_UNIFIED_VOLUME_SLIDER_CONTROLLER_H_
// 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/resources/vector_icons/vector_icons.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/audio/unified_volume_slider_controller.h"
using chromeos::CrasAudioHandler;
namespace ash {
namespace {
// Threshold to ignore update on the slider value.
const float kSliderIgnoreUpdateThreshold = 0.01;
// References to the icons that correspond to different volume levels.
const gfx::VectorIcon* const kVolumeLevelIcons[] = {
&kSystemMenuVolumeMuteIcon, // Muted.
&kSystemMenuVolumeLowIcon, // Low volume.
&kSystemMenuVolumeMediumIcon, // Medium volume.
&kSystemMenuVolumeHighIcon, // High volume.
&kSystemMenuVolumeHighIcon, // Full volume.
};
// The maximum index of kVolumeLevelIcons.
constexpr int kVolumeLevels = arraysize(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];
}
} // namespace
UnifiedVolumeView::UnifiedVolumeView(UnifiedVolumeSliderController* controller)
: UnifiedSliderView(controller,
kSystemMenuVolumeHighIcon,
IDS_ASH_STATUS_TRAY_VOLUME) {
DCHECK(CrasAudioHandler::IsInitialized());
CrasAudioHandler::Get()->AddAudioObserver(this);
Update();
}
UnifiedVolumeView::~UnifiedVolumeView() {
DCHECK(CrasAudioHandler::IsInitialized());
CrasAudioHandler::Get()->RemoveAudioObserver(this);
}
void UnifiedVolumeView::Update() {
bool is_muted = CrasAudioHandler::Get()->IsOutputMuted();
float level = CrasAudioHandler::Get()->GetOutputVolumePercent() / 100.f;
// Indicate that the slider is inactive when it's muted.
slider()->UpdateState(!is_muted);
button()->SetToggled(!is_muted);
button()->SetVectorIcon(GetVolumeIconForLevel(is_muted ? 0.f : level));
// 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, do not set change
// slider value if the change is less than the threshold.
if (std::abs(level - slider()->value()) < kSliderIgnoreUpdateThreshold)
return;
slider()->SetValue(level);
}
void UnifiedVolumeView::OnOutputNodeVolumeChanged(uint64_t node_id,
int volume) {
Update();
}
void UnifiedVolumeView::OnOutputMuteChanged(bool mute_on, bool system_adjust) {
Update();
}
void UnifiedVolumeView::OnAudioNodesChanged() {
Update();
}
void UnifiedVolumeView::OnActiveOutputNodeChanged() {
Update();
}
void UnifiedVolumeView::OnActiveInputNodeChanged() {
Update();
}
} // namespace ash
// 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.
#ifndef ASH_SYSTEM_AUDIO_UNIFIED_VOLUME_VIEW_H_
#define ASH_SYSTEM_AUDIO_UNIFIED_VOLUME_VIEW_H_
#include "ash/system/unified/unified_slider_view.h"
#include "chromeos/audio/cras_audio_handler.h"
namespace ash {
class UnifiedVolumeSliderController;
// View of a slider that can change audio volume.
class UnifiedVolumeView : public UnifiedSliderView,
public chromeos::CrasAudioHandler::AudioObserver {
public:
explicit UnifiedVolumeView(UnifiedVolumeSliderController* controller);
~UnifiedVolumeView() override;
private:
void Update();
// CrasAudioHandler::AudioObserver:
void OnOutputNodeVolumeChanged(uint64_t node_id, int volume) override;
void OnOutputMuteChanged(bool mute_on, bool system_adjust) override;
void OnAudioNodesChanged() override;
void OnActiveOutputNodeChanged() override;
void OnActiveInputNodeChanged() override;
DISALLOW_COPY_AND_ASSIGN(UnifiedVolumeView);
};
} // namespace ash
#endif // ASH_SYSTEM_AUDIO_UNIFIED_VOLUME_VIEW_H_
......@@ -7,17 +7,14 @@
#include <algorithm>
#include "ash/metrics/user_metrics_recorder.h"
#include "ash/public/cpp/ash_features.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/brightness_control_delegate.h"
#include "ash/system/status_area_widget.h"
#include "ash/system/tray/system_tray.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/tray/tray_popup_utils.h"
#include "ash/system/tray/tri_view.h"
#include "ash/system/unified/unified_system_tray.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "ash/wm/tablet_mode/tablet_mode_observer.h"
#include "base/bind.h"
......@@ -272,12 +269,4 @@ void TrayBrightness::HandleBrightnessChanged(double percent,
ShowDetailedView(kTrayPopupAutoCloseDelayInSeconds);
}
bool TrayBrightness::IsUnifiedBubbleShown() const {
return features::IsSystemTrayUnifiedEnabled() && system_tray()
->shelf()
->GetStatusAreaWidget()
->unified_system_tray()
->IsBubbleShown();
}
} // namespace ash
......@@ -57,9 +57,6 @@ class ASH_EXPORT TrayBrightness
void HandleBrightnessChanged(double percent, bool user_initiated);
// Returns true if the bubble of UnifiedSystemTray is shown.
bool IsUnifiedBubbleShown() const;
tray::BrightnessView* brightness_view_;
// Brightness level in the range [0.0, 100.0] that we've heard about most
......
......@@ -4,8 +4,11 @@
#include "ash/system/tray/system_tray_item.h"
#include "ash/public/cpp/ash_features.h"
#include "ash/system/status_area_widget.h"
#include "ash/system/tray/system_tray.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/unified/unified_system_tray.h"
#include "base/timer/timer.h"
#include "ui/views/view.h"
......@@ -62,4 +65,12 @@ bool SystemTrayItem::ShouldShowShelf() const {
return true;
}
bool SystemTrayItem::IsUnifiedBubbleShown() {
return features::IsSystemTrayUnifiedEnabled() && system_tray()
->shelf()
->GetStatusAreaWidget()
->unified_system_tray()
->IsBubbleShown();
}
} // namespace ash
......@@ -132,6 +132,9 @@ class ASH_EXPORT SystemTrayItem {
// the shelf is in the auto-hide state. Default is true.
virtual bool ShouldShowShelf() const;
// Returns true if the bubble of UnifiedSystemTray is shown.
bool IsUnifiedBubbleShown();
// Returns the system tray that this item belongs to.
SystemTray* system_tray() const { return system_tray_; }
......
......@@ -6,20 +6,54 @@
#include "ash/system/tray/tray_constants.h"
#include "ash/system/unified/top_shortcut_button.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/border.h"
#include "ui/views/layout/box_layout.h"
namespace ash {
UnifiedSliderButton::UnifiedSliderButton(views::ButtonListener* listener,
const gfx::VectorIcon& icon,
int accessible_name_id)
: TopShortcutButton(listener, icon, accessible_name_id) {}
UnifiedSliderButton::~UnifiedSliderButton() = default;
void UnifiedSliderButton::SetVectorIcon(const gfx::VectorIcon& icon) {
SetImage(views::Button::STATE_NORMAL,
gfx::CreateVectorIcon(icon, kUnifiedMenuIconColor));
SetImage(views::Button::STATE_DISABLED,
gfx::CreateVectorIcon(icon, kUnifiedMenuIconColor));
}
void UnifiedSliderButton::SetToggled(bool toggled) {
toggled_ = toggled;
SchedulePaint();
}
void UnifiedSliderButton::PaintButtonContents(gfx::Canvas* canvas) {
gfx::Rect rect(GetContentsBounds());
cc::PaintFlags flags;
flags.setAntiAlias(true);
flags.setColor(toggled_ ? kUnifiedMenuButtonColorActive
: kUnifiedMenuButtonColor);
flags.setStyle(cc::PaintFlags::kFill_Style);
canvas->DrawCircle(gfx::PointF(rect.CenterPoint()), kTrayItemSize / 2, flags);
views::ImageButton::PaintButtonContents(canvas);
}
UnifiedSliderView::UnifiedSliderView(UnifiedSliderListener* listener,
const gfx::VectorIcon& icon,
int accessible_name_id)
: slider_(new views::Slider(listener)) {
: button_(new UnifiedSliderButton(listener, icon, accessible_name_id)),
slider_(new views::Slider(listener)) {
auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::kHorizontal, kUnifiedMenuItemPadding,
kUnifiedTopShortcutSpacing));
AddChildView(new TopShortcutButton(listener, icon, accessible_name_id));
AddChildView(button_);
AddChildView(slider_);
slider_->SetBorder(views::CreateEmptyBorder(kUnifiedSliderPadding));
......
......@@ -5,6 +5,7 @@
#ifndef ASH_SYSTEM_UNIFIED_UNIFIED_SLIDER_VIEW_H_
#define ASH_SYSTEM_UNIFIED_UNIFIED_SLIDER_VIEW_H_
#include "ash/system/unified/top_shortcut_button.h"
#include "ui/gfx/vector_icon_types.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/slider.h"
......@@ -18,6 +19,30 @@ class UnifiedSliderListener : public views::ButtonListener,
~UnifiedSliderListener() override = default;
};
// A button used in a slider row of UnifiedSystemTray. The button is togglable.
class UnifiedSliderButton : public TopShortcutButton {
public:
UnifiedSliderButton(views::ButtonListener* listener,
const gfx::VectorIcon& icon,
int accessible_name_id);
~UnifiedSliderButton() override;
// Set the vector icon shown in a circle.
void SetVectorIcon(const gfx::VectorIcon& icon);
// Change the toggle state.
void SetToggled(bool toggled);
// TopShortcutButton:
void PaintButtonContents(gfx::Canvas* canvas) override;
private:
// Ture if the button is currently toggled.
bool toggled_ = false;
DISALLOW_COPY_AND_ASSIGN(UnifiedSliderButton);
};
// Base view class of a slider row in UnifiedSystemTray. It has a button on the
// left side and a slider on the right side.
class UnifiedSliderView : public views::View {
......@@ -28,10 +53,12 @@ class UnifiedSliderView : public views::View {
~UnifiedSliderView() override;
protected:
UnifiedSliderButton* button() { return button_; }
views::Slider* slider() { return slider_; }
private:
// Unowned. Owned by views hierarchy.
UnifiedSliderButton* const button_;
views::Slider* const slider_;
DISALLOW_COPY_AND_ASSIGN(UnifiedSliderView);
......
......@@ -8,6 +8,7 @@
#include "ash/metrics/user_metrics_recorder.h"
#include "ash/session/session_controller.h"
#include "ash/shell.h"
#include "ash/system/audio/unified_volume_slider_controller.h"
#include "ash/system/bluetooth/bluetooth_feature_pod_controller.h"
#include "ash/system/bluetooth/tray_bluetooth.h"
#include "ash/system/brightness/unified_brightness_slider_controller.h"
......@@ -39,6 +40,9 @@ UnifiedSystemTrayView* UnifiedSystemTrayController::CreateView() {
unified_view_ = new UnifiedSystemTrayView(this);
InitFeaturePods();
volume_slider_controller_ = std::make_unique<UnifiedVolumeSliderController>();
unified_view_->AddSliderView(volume_slider_controller_->CreateView());
brightness_slider_controller_ =
std::make_unique<UnifiedBrightnessSliderController>();
unified_view_->AddSliderView(brightness_slider_controller_->CreateView());
......
......@@ -16,6 +16,7 @@ namespace ash {
class FeaturePodControllerBase;
class SystemTray;
class UnifiedBrightnessSliderController;
class UnifiedVolumeSliderController;
class UnifiedSystemTrayView;
// Controller class of UnifiedSystemTrayView. Handles events of the view.
......@@ -67,6 +68,10 @@ class ASH_EXPORT UnifiedSystemTrayController {
std::vector<std::unique_ptr<FeaturePodControllerBase>>
feature_pod_controllers_;
// Controller of volume slider. Owned.
std::unique_ptr<UnifiedVolumeSliderController> volume_slider_controller_;
// Controller of brightness slider. Owned.
std::unique_ptr<UnifiedBrightnessSliderController>
brightness_slider_controller_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment