summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ash/ash.gyp4
-rw-r--r--ash/ash_strings.grd9
-rw-r--r--ash/ash_switches.cc3
-rw-r--r--ash/ash_switches.h1
-rw-r--r--ash/resources/ash_resources.grd5
-rw-r--r--ash/system/chromeos/audio/audio_observer.h2
-rw-r--r--ash/system/chromeos/audio/tray_audio.cc612
-rw-r--r--ash/system/chromeos/audio/tray_audio.h (renamed from ash/system/chromeos/audio/tray_volume.h)36
-rw-r--r--ash/system/chromeos/audio/tray_volume.cc288
-rw-r--r--ash/system/tray/system_tray.cc4
-rw-r--r--chrome/app/generated_resources.grd6
-rw-r--r--chrome/browser/about_flags.cc7
-rw-r--r--chrome/browser/chromeos/chrome_browser_main_chromeos.cc19
-rw-r--r--chrome/browser/chromeos/login/chrome_restart_request.cc1
-rw-r--r--chrome/browser/chromeos/system/ash_system_tray_delegate.cc14
-rw-r--r--chrome/browser/ui/ash/volume_controller_chromeos.cc62
-rw-r--r--chrome/browser/ui/ash/volume_controller_chromeos.h14
-rw-r--r--chromeos/audio/audio_device.cc85
-rw-r--r--chromeos/audio/audio_device.h43
-rw-r--r--chromeos/audio/cras_audio_handler.cc49
-rw-r--r--chromeos/audio/cras_audio_handler.h14
-rw-r--r--chromeos/chromeos.gyp2
-rw-r--r--chromeos/dbus/cras_audio_client.cc67
23 files changed, 1018 insertions, 329 deletions
diff --git a/ash/ash.gyp b/ash/ash.gyp
index d53debc..d38804c 100644
--- a/ash/ash.gyp
+++ b/ash/ash.gyp
@@ -184,8 +184,8 @@
'system/brightness/tray_brightness.cc',
'system/brightness/tray_brightness.h',
'system/chromeos/audio/audio_observer.h',
- 'system/chromeos/audio/tray_volume.cc',
- 'system/chromeos/audio/tray_volume.h',
+ 'system/chromeos/audio/tray_audio.cc',
+ 'system/chromeos/audio/tray_audio.h',
'system/chromeos/enterprise/enterprise_domain_observer.h',
'system/chromeos/enterprise/tray_enterprise.h',
'system/chromeos/enterprise/tray_enterprise.cc',
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 14de4a9..0dbb598 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -284,6 +284,15 @@ Press Ctrl+Alt+Z to disable.
<message name="IDS_ASH_STATUS_TRAY_VOLUME" desc="The accessible text for the volume slider.">
Volume
</message>
+ <message name="IDS_ASH_STATUS_TRAY_AUDIO" desc="The label used in audio detailed page bottom header of ash tray pop up.">
+ Audio Settings
+ </message>
+ <message name="IDS_ASH_STATUS_TRAY_AUDIO_OUTPUT" desc="The label used in audio detailed page for audio output section of ash tray pop up.">
+ OUTPUT
+ </message>
+ <message name="IDS_ASH_STATUS_TRAY_AUDIO_INPUT" desc="The label used in audio detailed page for audio input section of ash tray pop up.">
+ INPUT
+ </message>
<message name="IDS_ASH_STATUS_TRAY_DISPLAY_MIRRORING" desc="The label used in the tray to show that the current status is mirroring.">
Mirroring to <ph name="DISPLAY_NAME">$1</ph>
diff --git a/ash/ash_switches.cc b/ash/ash_switches.cc
index bbbc3db..b6908a7 100644
--- a/ash/ash_switches.cc
+++ b/ash/ash_switches.cc
@@ -78,6 +78,9 @@ const char kAshEnableAdvancedGestures[] = "ash-enable-advanced-gestures";
// main monitor as internal.
const char kAshEnableBrightnessControl[] = "ash-enable-brightness-control";
+// Enable the new audio handler.
+const char kAshEnableNewAudioHandler[] = "ash-enable-new-audio-handler";
+
// Enable immersive fullscreen mode, regardless of default setting.
const char kAshEnableImmersiveFullscreen[] = "ash-enable-immersive-fullscreen";
diff --git a/ash/ash_switches.h b/ash/ash_switches.h
index b0ceae2..a09f06d 100644
--- a/ash/ash_switches.h
+++ b/ash/ash_switches.h
@@ -36,6 +36,7 @@ ASH_EXPORT extern const char kAshDisableUIScaling[];
ASH_EXPORT extern const char kAshDisableDisplayRotation[];
ASH_EXPORT extern const char kAshEnableAdvancedGestures[];
ASH_EXPORT extern const char kAshEnableBrightnessControl[];
+ASH_EXPORT extern const char kAshEnableNewAudioHandler[];
#if defined(OS_LINUX)
ASH_EXPORT extern const char kAshEnableMemoryMonitor[];
#endif
diff --git a/ash/resources/ash_resources.grd b/ash/resources/ash_resources.grd
index 47aca7f..f1d145c 100644
--- a/ash/resources/ash_resources.grd
+++ b/ash/resources/ash_resources.grd
@@ -163,6 +163,11 @@
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_VOLUME_LEVELS" file="cros/status/status_volume_dark.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_VOLUME_MUTE" file="cros/status/status_volume_mute.png" />
+ <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_AUDIO_HEADPHONE" file="cros/status/status_audio_device_headphones.png" />
+ <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_AUDIO_USB" file="cros/status/status_audio_device_usb.png" />
+ <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_AUDIO_BLUETOOTH" file="cros/status/status_audio_device_bluetooth.png" />
+ <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_AUDIO_HDMI" file="cros/status/status_audio_device_hdmi.png" />
+
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_WIFI_DISABLED" file="cros/network/status_wifi_disabled.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_WIFI_DISABLED_HOVER" file="cros/network/status_wifi_disabled_hover.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_WIFI_ENABLED" file="cros/network/status_wifi_enabled.png" />
diff --git a/ash/system/chromeos/audio/audio_observer.h b/ash/system/chromeos/audio/audio_observer.h
index 9d529c0..0c3f5d7 100644
--- a/ash/system/chromeos/audio/audio_observer.h
+++ b/ash/system/chromeos/audio/audio_observer.h
@@ -9,6 +9,8 @@
namespace ash {
+// TODO(jennyz): crbug.com/233310. Remove this file when new audio handler
+// stabilized.
class ASH_EXPORT AudioObserver {
public:
virtual ~AudioObserver() {}
diff --git a/ash/system/chromeos/audio/tray_audio.cc b/ash/system/chromeos/audio/tray_audio.cc
new file mode 100644
index 0000000..ea0cb94a
--- /dev/null
+++ b/ash/system/chromeos/audio/tray_audio.cc
@@ -0,0 +1,612 @@
+// Copyright (c) 2012 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/chromeos/audio/tray_audio.h"
+
+#include <cmath>
+
+#include "ash/ash_constants.h"
+#include "ash/ash_switches.h"
+#include "ash/shell.h"
+#include "ash/system/tray/actionable_view.h"
+#include "ash/system/tray/fixed_sized_scroll_view.h"
+#include "ash/system/tray/hover_highlight_view.h"
+#include "ash/system/tray/system_tray.h"
+#include "ash/system/tray/system_tray_delegate.h"
+#include "ash/system/tray/system_tray_notifier.h"
+#include "ash/system/tray/tray_constants.h"
+#include "ash/volume_control_delegate.h"
+#include "base/command_line.h"
+#include "base/utf_string_conversions.h"
+#include "chromeos/audio/cras_audio_handler.h"
+#include "grit/ash_resources.h"
+#include "grit/ash_strings.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkPaint.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "third_party/skia/include/effects/SkGradientShader.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/image/image.h"
+#include "ui/gfx/image/image_skia_operations.h"
+#include "ui/views/controls/button/image_button.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/controls/slider.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/view.h"
+
+namespace ash {
+namespace internal {
+
+namespace {
+const int kVolumeImageWidth = 25;
+const int kVolumeImageHeight = 25;
+const int kBarSeparatorWidth = 25;
+const int kBarSeparatorHeight = 30;
+const int kSliderRightPaddingToVolumeViewEdge = 17;
+const int kExtraPaddingBetweenBarAndMore = 10;
+
+const int kNoAudioDeviceIcon = -1;
+
+// IDR_AURA_UBER_TRAY_VOLUME_LEVELS contains 5 images,
+// The one for mute is at the 0 index and the other
+// four are used for ascending volume levels.
+const int kVolumeLevels = 4;
+
+bool UseNewAudioHandler() {
+ return CommandLine::ForCurrentProcess()->
+ HasSwitch(ash::switches::kAshEnableNewAudioHandler);
+}
+
+bool IsAudioMuted() {
+ if(UseNewAudioHandler()) {
+ return chromeos::CrasAudioHandler::Get()->IsOutputMuted();
+ } else {
+ return Shell::GetInstance()->system_tray_delegate()->
+ GetVolumeControlDelegate()->IsAudioMuted();
+ }
+}
+
+float GetVolumeLevel() {
+ if (UseNewAudioHandler()) {
+ return chromeos::CrasAudioHandler::Get()->GetOutputVolumePercent() / 100.0f;
+ } else {
+ return Shell::GetInstance()->system_tray_delegate()->
+ GetVolumeControlDelegate()->GetVolumeLevel();
+ }
+}
+
+int GetAudioDeviceIconId(chromeos::AudioDeviceType type) {
+ if (type == chromeos::AUDIO_TYPE_HEADPHONE)
+ return IDR_AURA_UBER_TRAY_AUDIO_HEADPHONE;
+ else if (type == chromeos::AUDIO_TYPE_USB)
+ return IDR_AURA_UBER_TRAY_AUDIO_USB;
+ else if (type == chromeos::AUDIO_TYPE_BLUETOOTH)
+ return IDR_AURA_UBER_TRAY_AUDIO_BLUETOOTH;
+ else if (type == chromeos::AUDIO_TYPE_HDMI)
+ return IDR_AURA_UBER_TRAY_AUDIO_HDMI;
+ else
+ return kNoAudioDeviceIcon;
+}
+
+} // namespace
+
+namespace tray {
+
+class VolumeButton : public views::ToggleImageButton {
+ public:
+ explicit VolumeButton(views::ButtonListener* listener)
+ : views::ToggleImageButton(listener),
+ image_index_(-1) {
+ SetImageAlignment(ALIGN_CENTER, ALIGN_MIDDLE);
+ image_ = ui::ResourceBundle::GetSharedInstance().GetImageNamed(
+ IDR_AURA_UBER_TRAY_VOLUME_LEVELS);
+ SetPreferredSize(gfx::Size(kTrayPopupItemHeight, kTrayPopupItemHeight));
+ Update();
+ }
+
+ virtual ~VolumeButton() {}
+
+ void Update() {
+ float level = GetVolumeLevel();
+ int image_index = IsAudioMuted() ?
+ 0 : (level == 1.0 ?
+ kVolumeLevels :
+ std::max(1, int(std::ceil(level * (kVolumeLevels - 1)))));
+ if (image_index != image_index_) {
+ gfx::Rect region(0, image_index * kVolumeImageHeight,
+ kVolumeImageWidth, kVolumeImageHeight);
+ gfx::ImageSkia image_skia = gfx::ImageSkiaOperations::ExtractSubset(
+ *(image_.ToImageSkia()), region);
+ SetImage(views::CustomButton::STATE_NORMAL, &image_skia);
+ image_index_ = image_index;
+ }
+ SchedulePaint();
+ }
+
+ private:
+ // Overridden from views::View.
+ virtual gfx::Size GetPreferredSize() OVERRIDE {
+ gfx::Size size = views::ToggleImageButton::GetPreferredSize();
+ size.set_height(kTrayPopupItemHeight);
+ return size;
+ }
+
+ gfx::Image image_;
+ int image_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(VolumeButton);
+};
+
+class VolumeSlider : public views::Slider {
+ public:
+ explicit VolumeSlider(views::SliderListener* listener)
+ : views::Slider(listener, views::Slider::HORIZONTAL) {
+ set_focus_border_color(kFocusBorderColor);
+ SetValue(GetVolumeLevel());
+ SetAccessibleName(
+ ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
+ IDS_ASH_STATUS_TRAY_VOLUME));
+ Update();
+ }
+ virtual ~VolumeSlider() {}
+
+ void Update() {
+ UpdateState(!IsAudioMuted());
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(VolumeSlider);
+};
+
+// Vertical bar separator that can be placed on the VolumeView.
+class BarSeparator : public views::View {
+ public:
+ BarSeparator() {}
+ virtual ~BarSeparator() {}
+
+ private:
+ // Overriden from views::View.
+ virtual gfx::Size GetPreferredSize() OVERRIDE {
+ return gfx::Size(kBarSeparatorWidth, kBarSeparatorHeight);
+ }
+
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
+ canvas->FillRect(gfx::Rect(width() / 2, 0, 1, height()),
+ kButtonStrokeColor);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(BarSeparator);
+};
+
+class VolumeView : public ActionableView,
+ public views::ButtonListener,
+ public views::SliderListener {
+ public:
+ VolumeView(SystemTrayItem* owner, bool is_default_view)
+ : owner_(owner),
+ icon_(NULL),
+ slider_(NULL),
+ bar_(NULL),
+ device_type_(NULL),
+ more_(NULL),
+ is_default_view_(is_default_view) {
+ SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal,
+ kTrayPopupPaddingHorizontal, 0, kTrayPopupPaddingBetweenItems));
+
+ icon_ = new VolumeButton(this);
+ AddChildView(icon_);
+
+ slider_ = new VolumeSlider(this);
+ AddChildView(slider_);
+
+ device_type_ = new views::ImageView;
+ AddChildView(device_type_);
+
+ bar_ = new BarSeparator;
+ AddChildView(bar_);
+
+ more_ = new views::ImageView;
+ more_->EnableCanvasFlippingForRTLUI(true);
+ more_->SetImage(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
+ IDR_AURA_UBER_TRAY_MORE).ToImageSkia());
+ AddChildView(more_);
+
+ Update();
+ }
+
+ virtual ~VolumeView() {}
+
+ void Update() {
+ icon_->Update();
+ slider_->Update();
+ UpdateDeviceTypeAndMore();
+ Layout();
+ }
+
+ void SetVolumeLevel(float percent) {
+ // The change in volume will be reflected via accessibility system events,
+ // so we prevent the UI event from being sent here.
+ slider_->set_enable_accessibility_events(false);
+ slider_->SetValue(percent);
+ // It is possible that the volume was (un)muted, but the actual volume level
+ // did not change. In that case, setting the value of the slider won't
+ // trigger an update. So explicitly trigger an update.
+ Update();
+ slider_->set_enable_accessibility_events(true);
+ }
+
+ private:
+ // Updates bar_, device_type_ icon, and more_ buttons.
+ void UpdateDeviceTypeAndMore() {
+ if (!UseNewAudioHandler() || !is_default_view_) {
+ more_->SetVisible(false);
+ bar_->SetVisible(false);
+ device_type_->SetVisible(false);
+ return;
+ }
+
+ chromeos::CrasAudioHandler* audio_handler =
+ chromeos::CrasAudioHandler::Get();
+ bool show_more = audio_handler->has_alternative_output() ||
+ audio_handler->has_alternative_input();
+ more_->SetVisible(show_more);
+
+ // Show output device icon if necessary.
+ chromeos::AudioDevice device;
+ audio_handler->GetActiveOutputDevice(&device);
+ int device_icon = GetAudioDeviceIconId(device.type);
+ if (device_icon != kNoAudioDeviceIcon) {
+ device_type_->SetVisible(true);
+ device_type_->SetImage(
+ ui::ResourceBundle::GetSharedInstance().GetImageNamed(
+ device_icon).ToImageSkia());
+ bar_->SetVisible(false);
+ } else {
+ device_type_->SetVisible(false);
+ bar_->SetVisible(show_more);
+ }
+ }
+
+ // Overridden from views::View.
+ virtual void Layout() OVERRIDE {
+ views::View::Layout();
+
+ if (!more_->visible()) {
+ int w = width() - slider_->bounds().x() -
+ kSliderRightPaddingToVolumeViewEdge;
+ slider_->SetSize(gfx::Size(w, slider_->height()));
+ return;
+ }
+
+ // Make sure the chevron always has the full size.
+ gfx::Size size = more_->GetPreferredSize();
+ gfx::Rect bounds(size);
+ bounds.set_x(width() - size.width() - kTrayPopupPaddingBetweenItems);
+ bounds.set_y((height() - size.height()) / 2);
+ more_->SetBoundsRect(bounds);
+
+ // Layout bar_ or device_type_ at the left of the more_ button.
+ views::View* view_left_to_more;
+ if (bar_->visible())
+ view_left_to_more = bar_;
+ else
+ view_left_to_more = device_type_;
+ gfx::Size bar_size = view_left_to_more->GetPreferredSize();
+ gfx::Rect bar_bounds(bar_size);
+ bar_bounds.set_x(more_->bounds().x() - bar_size.width() -
+ kExtraPaddingBetweenBarAndMore);
+ bar_bounds.set_y((height() - bar_size.height()) / 2);
+ view_left_to_more->SetBoundsRect(bar_bounds);
+
+
+ // Layout slider, calculate slider width.
+ gfx::Rect slider_bounds = slider_->bounds();
+ slider_bounds.set_width(
+ view_left_to_more->bounds().x() - kTrayPopupPaddingBetweenItems
+ - slider_bounds.x());
+ slider_->SetBoundsRect(slider_bounds);
+ }
+
+ // Overridden from views::ButtonListener.
+ virtual void ButtonPressed(views::Button* sender,
+ const ui::Event& event) OVERRIDE {
+ CHECK(sender == icon_);
+ if (UseNewAudioHandler()) {
+ chromeos::CrasAudioHandler::Get()->SetOutputMute(!IsAudioMuted());
+ } else {
+ ash::Shell::GetInstance()->system_tray_delegate()->
+ GetVolumeControlDelegate()->SetAudioMuted(!IsAudioMuted());
+ }
+ }
+
+ // Overridden from views:SliderListener.
+ virtual void SliderValueChanged(views::Slider* sender,
+ float value,
+ float old_value,
+ views::SliderChangeReason reason) OVERRIDE {
+ if (reason == views::VALUE_CHANGED_BY_USER) {
+ if (UseNewAudioHandler()) {
+ chromeos::CrasAudioHandler::Get()->
+ SetOutputVolumePercent(value * 100.0f);
+ }
+ else {
+ ash::Shell::GetInstance()->system_tray_delegate()->
+ GetVolumeControlDelegate()->SetVolumeLevel(value);
+ }
+ }
+ icon_->Update();
+ }
+
+ // Overriden from ActinableView.
+ virtual bool PerformAction(const ui::Event& event) OVERRIDE {
+ if (!more_->visible())
+ return false;
+ owner_->TransitionDetailedView();
+ return true;
+ }
+
+ SystemTrayItem* owner_;
+ VolumeButton* icon_;
+ VolumeSlider* slider_;
+ BarSeparator* bar_;
+ views::ImageView* device_type_;
+ views::ImageView* more_;
+ bool is_default_view_;
+
+ DISALLOW_COPY_AND_ASSIGN(VolumeView);
+};
+
+class AudioDetailedView : public TrayDetailsView,
+ public ViewClickListener {
+ public:
+ AudioDetailedView(SystemTrayItem* owner, user::LoginStatus login)
+ : TrayDetailsView(owner),
+ login_(login) {
+ CreateItems();
+ Update();
+ }
+
+ virtual ~AudioDetailedView() {
+ }
+
+ void Update() {
+ UpdateAudioDevices();
+ Layout();
+ }
+
+ private:
+ void CreateItems() {
+ CreateScrollableList();
+ CreateHeaderEntry();
+ }
+
+ void CreateHeaderEntry() {
+ CreateSpecialRow(IDS_ASH_STATUS_TRAY_AUDIO, this);
+ }
+
+ void UpdateAudioDevices() {
+ output_devices_.clear();
+ input_devices_.clear();
+ chromeos::AudioDeviceList devices;
+ chromeos::CrasAudioHandler::Get()->GetAudioDevices(&devices);
+ for (size_t i = 0; i < devices.size(); ++i) {
+ if (devices[i].is_input)
+ input_devices_.push_back(devices[i]);
+ else
+ output_devices_.push_back(devices[i]);
+ }
+ UpdateScrollableList();
+ }
+
+ void UpdateScrollableList() {
+ scroll_content()->RemoveAllChildViews(true);
+ device_map_.clear();
+
+ // Add audio output devices.
+ AddScrollListItem(
+ l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_OUTPUT),
+ gfx::Font::BOLD,
+ false); /* no checkmark */
+ for (size_t i = 0; i < output_devices_.size(); ++i) {
+ HoverHighlightView* container = AddScrollListItem(
+ output_devices_[i].display_name,
+ gfx::Font::NORMAL,
+ output_devices_[i].active); /* checkmark if active */
+ device_map_[container] = output_devices_[i];
+ }
+
+ AddScrollSeparator();
+
+ // Add audio input devices.
+ AddScrollListItem(
+ l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_INPUT),
+ gfx::Font::BOLD,
+ false); /* no checkmark */
+ for (size_t i = 0; i < input_devices_.size(); ++i) {
+ HoverHighlightView* container = AddScrollListItem(
+ input_devices_[i].display_name,
+ gfx::Font::NORMAL,
+ input_devices_[i].active); /* checkmark if active */
+ device_map_[container] = input_devices_[i];
+ }
+
+ scroll_content()->SizeToPreferredSize();
+ scroller()->Layout();
+ }
+
+ HoverHighlightView* AddScrollListItem(const string16& text,
+ gfx::Font::FontStyle style,
+ bool checked) {
+ HoverHighlightView* container = new HoverHighlightView(this);
+ container->AddCheckableLabel(text, style, checked);
+ scroll_content()->AddChildView(container);
+ return container;
+ }
+
+ // Overridden from ViewClickListener.
+ virtual void OnViewClicked(views::View* sender) OVERRIDE {
+ if (sender == footer()->content()) {
+ owner()->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+ } else {
+ AudioDeviceMap::iterator iter = device_map_.find(sender);
+ if (iter == device_map_.end())
+ return;
+ chromeos::AudioDevice& device = iter->second;
+ if (device.is_input)
+ chromeos::CrasAudioHandler::Get()->SetActiveInputNode(device.id);
+ else
+ chromeos::CrasAudioHandler::Get()->SetActiveOutputNode(device.id);
+ }
+ }
+
+ typedef std::map<views::View*, chromeos::AudioDevice> AudioDeviceMap;
+
+ user::LoginStatus login_;
+ chromeos::AudioDeviceList output_devices_;
+ chromeos::AudioDeviceList input_devices_;
+ AudioDeviceMap device_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioDetailedView);
+};
+
+} // namespace tray
+
+TrayAudio::TrayAudio(SystemTray* system_tray)
+ : TrayImageItem(system_tray, IDR_AURA_UBER_TRAY_VOLUME_MUTE),
+ volume_view_(NULL),
+ audio_detail_(NULL),
+ pop_up_volume_view_(false) {
+ if (UseNewAudioHandler())
+ chromeos::CrasAudioHandler::Get()->AddAudioObserver(this);
+ else
+ Shell::GetInstance()->system_tray_notifier()->AddAudioObserver(this);
+}
+
+TrayAudio::~TrayAudio() {
+ if (UseNewAudioHandler()) {
+ if (chromeos::CrasAudioHandler::IsInitialized())
+ chromeos::CrasAudioHandler::Get()->RemoveAudioObserver(this);
+ } else {
+ Shell::GetInstance()->system_tray_notifier()->RemoveAudioObserver(this);
+ }
+}
+
+bool TrayAudio::GetInitialVisibility() {
+ return IsAudioMuted();
+}
+
+views::View* TrayAudio::CreateDefaultView(user::LoginStatus status) {
+ volume_view_ = new tray::VolumeView(this, true);
+ return volume_view_;
+}
+
+views::View* TrayAudio::CreateDetailedView(user::LoginStatus status) {
+ if (!UseNewAudioHandler() || pop_up_volume_view_) {
+ volume_view_ = new tray::VolumeView(this, false);
+ return volume_view_;
+ } else {
+ audio_detail_ = new tray::AudioDetailedView(this, status);
+ return audio_detail_;
+ }
+}
+
+void TrayAudio::DestroyDefaultView() {
+ volume_view_ = NULL;
+}
+
+void TrayAudio::DestroyDetailedView() {
+ if (audio_detail_) {
+ audio_detail_ = NULL;
+ } else if (volume_view_) {
+ volume_view_ = NULL;
+ pop_up_volume_view_ = false;
+ }
+}
+
+bool TrayAudio::ShouldHideArrow() const {
+ return true;
+}
+
+bool TrayAudio::ShouldShowLauncher() const {
+ return false;
+}
+
+void TrayAudio::OnVolumeChanged(float percent) {
+ DCHECK(!UseNewAudioHandler());
+ if (tray_view())
+ tray_view()->SetVisible(GetInitialVisibility());
+
+ if (volume_view_) {
+ if (IsAudioMuted())
+ percent = 0.0;
+ volume_view_->SetVolumeLevel(percent);
+ SetDetailedViewCloseDelay(kTrayPopupAutoCloseDelayInSeconds);
+ return;
+ }
+ PopupDetailedView(kTrayPopupAutoCloseDelayInSeconds, false);
+}
+
+void TrayAudio::OnMuteToggled() {
+ DCHECK(!UseNewAudioHandler());
+ if (tray_view())
+ tray_view()->SetVisible(GetInitialVisibility());
+
+ if (volume_view_)
+ volume_view_->Update();
+ else
+ PopupDetailedView(kTrayPopupAutoCloseDelayInSeconds, false);
+}
+
+
+void TrayAudio::OnOutputVolumeChanged() {
+ DCHECK(UseNewAudioHandler());
+ float percent = GetVolumeLevel();
+ if (tray_view())
+ tray_view()->SetVisible(GetInitialVisibility());
+
+ if (volume_view_) {
+ volume_view_->SetVolumeLevel(percent);
+ SetDetailedViewCloseDelay(kTrayPopupAutoCloseDelayInSeconds);
+ return;
+ }
+ pop_up_volume_view_ = true;
+ PopupDetailedView(kTrayPopupAutoCloseDelayInSeconds, false);
+}
+
+void TrayAudio::OnOutputMuteChanged() {
+ DCHECK(UseNewAudioHandler());
+ if (tray_view())
+ tray_view()->SetVisible(GetInitialVisibility());
+
+ if (volume_view_)
+ volume_view_->Update();
+ else {
+ pop_up_volume_view_ = true;
+ PopupDetailedView(kTrayPopupAutoCloseDelayInSeconds, false);
+ }
+}
+
+void TrayAudio::OnAudioNodesChanged() {
+ Update();
+}
+
+void TrayAudio::OnActiveOutputNodeChanged() {
+ Update();
+}
+
+void TrayAudio::OnActiveInputNodeChanged() {
+ Update();
+}
+
+void TrayAudio::Update() {
+ if (audio_detail_)
+ audio_detail_->Update();
+ if (volume_view_)
+ volume_view_->Update();
+}
+
+} // namespace internal
+} // namespace ash
diff --git a/ash/system/chromeos/audio/tray_volume.h b/ash/system/chromeos/audio/tray_audio.h
index 601aa39..af8f7a9 100644
--- a/ash/system/chromeos/audio/tray_volume.h
+++ b/ash/system/chromeos/audio/tray_audio.h
@@ -2,24 +2,27 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef ASH_SYSTEM_CHROMEOS_AUDIO_TRAY_VOLUME_H_
-#define ASH_SYSTEM_CHROMEOS_AUDIO_TRAY_VOLUME_H_
+#ifndef ASH_SYSTEM_CHROMEOS_AUDIO_TRAY_AUDIO_H_
+#define ASH_SYSTEM_CHROMEOS_AUDIO_TRAY_AUDIO_H_
#include "ash/system/chromeos/audio/audio_observer.h"
#include "ash/system/tray/tray_image_item.h"
+#include "chromeos/audio/cras_audio_handler.h"
namespace ash {
namespace internal {
namespace tray {
class VolumeView;
+class AudioDetailedView;
}
-class TrayVolume : public TrayImageItem,
- public AudioObserver {
+class TrayAudio : public TrayImageItem,
+ public chromeos::CrasAudioHandler::AudioObserver,
+ public AudioObserver {
public:
- explicit TrayVolume(SystemTray* system_tray);
- virtual ~TrayVolume();
+ explicit TrayAudio(SystemTray* system_tray);
+ virtual ~TrayAudio();
private:
// Overridden from TrayImageItem.
@@ -37,17 +40,26 @@ class TrayVolume : public TrayImageItem,
virtual void OnVolumeChanged(float percent) OVERRIDE;
virtual void OnMuteToggled() OVERRIDE;
+ // Overridden from chromeos::CrasAudioHandler::AudioObserver.
+ virtual void OnOutputVolumeChanged() OVERRIDE;
+ virtual void OnOutputMuteChanged() OVERRIDE;
+ virtual void OnAudioNodesChanged() OVERRIDE;
+ virtual void OnActiveOutputNodeChanged() OVERRIDE;
+ virtual void OnActiveInputNodeChanged() OVERRIDE;
+
+ void Update();
+
tray::VolumeView* volume_view_;
+ tray::AudioDetailedView* audio_detail_;
- // Was |volume_view_| created for CreateDefaultView() rather than
- // CreateDetailedView()? Used to avoid resetting |volume_view_|
- // inappropriately in DestroyDefaultView() or DestroyDetailedView().
- bool is_default_view_;
+ // True if VolumeView should be created for accelerator pop up;
+ // Otherwise, it should be created for detailed view in ash tray bubble.
+ bool pop_up_volume_view_;
- DISALLOW_COPY_AND_ASSIGN(TrayVolume);
+ DISALLOW_COPY_AND_ASSIGN(TrayAudio);
};
} // namespace internal
} // namespace ash
-#endif // ASH_SYSTEM_CHROMEOS_AUDIO_TRAY_VOLUME_H_
+#endif // ASH_SYSTEM_CHROMEOS_AUDIO_TRAY_AUDIO_H_
diff --git a/ash/system/chromeos/audio/tray_volume.cc b/ash/system/chromeos/audio/tray_volume.cc
deleted file mode 100644
index 7551a4b..0000000
--- a/ash/system/chromeos/audio/tray_volume.cc
+++ /dev/null
@@ -1,288 +0,0 @@
-// Copyright (c) 2012 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/chromeos/audio/tray_volume.h"
-
-#include <cmath>
-
-#include "ash/ash_constants.h"
-#include "ash/shell.h"
-#include "ash/system/tray/system_tray_delegate.h"
-#include "ash/system/tray/system_tray_notifier.h"
-#include "ash/system/tray/tray_bar_button_with_title.h"
-#include "ash/system/tray/tray_constants.h"
-#include "ash/volume_control_delegate.h"
-#include "base/utf_string_conversions.h"
-#include "grit/ash_resources.h"
-#include "grit/ash_strings.h"
-#include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkPaint.h"
-#include "third_party/skia/include/core/SkRect.h"
-#include "third_party/skia/include/effects/SkGradientShader.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/image/image.h"
-#include "ui/gfx/image/image_skia_operations.h"
-#include "ui/views/controls/button/image_button.h"
-#include "ui/views/controls/image_view.h"
-#include "ui/views/controls/label.h"
-#include "ui/views/controls/slider.h"
-#include "ui/views/layout/box_layout.h"
-#include "ui/views/view.h"
-
-namespace ash {
-namespace internal {
-
-namespace {
-const int kVolumeImageWidth = 25;
-const int kVolumeImageHeight = 25;
-
-// IDR_AURA_UBER_TRAY_VOLUME_LEVELS contains 5 images,
-// The one for mute is at the 0 index and the other
-// four are used for ascending volume levels.
-const int kVolumeLevels = 4;
-
-bool IsAudioMuted() {
- return Shell::GetInstance()->system_tray_delegate()->
- GetVolumeControlDelegate()->IsAudioMuted();
-}
-
-float GetVolumeLevel() {
- return Shell::GetInstance()->system_tray_delegate()->
- GetVolumeControlDelegate()->GetVolumeLevel();
-}
-
-} // namespace
-
-namespace tray {
-
-class VolumeButton : public views::ToggleImageButton {
- public:
- explicit VolumeButton(views::ButtonListener* listener)
- : views::ToggleImageButton(listener),
- image_index_(-1) {
- SetImageAlignment(ALIGN_CENTER, ALIGN_MIDDLE);
- image_ = ui::ResourceBundle::GetSharedInstance().GetImageNamed(
- IDR_AURA_UBER_TRAY_VOLUME_LEVELS);
- SetPreferredSize(gfx::Size(kTrayPopupItemHeight, kTrayPopupItemHeight));
- Update();
- }
-
- virtual ~VolumeButton() {}
-
- void Update() {
- float level = GetVolumeLevel();
- int image_index = IsAudioMuted() ?
- 0 : (level == 1.0 ?
- kVolumeLevels :
- std::max(1, int(std::ceil(level * (kVolumeLevels - 1)))));
- if (image_index != image_index_) {
- gfx::Rect region(0, image_index * kVolumeImageHeight,
- kVolumeImageWidth, kVolumeImageHeight);
- gfx::ImageSkia image_skia = gfx::ImageSkiaOperations::ExtractSubset(
- *(image_.ToImageSkia()), region);
- SetImage(views::CustomButton::STATE_NORMAL, &image_skia);
- image_index_ = image_index;
- }
- SchedulePaint();
- }
-
- private:
- // Overridden from views::View.
- virtual gfx::Size GetPreferredSize() OVERRIDE {
- gfx::Size size = views::ToggleImageButton::GetPreferredSize();
- size.set_height(kTrayPopupItemHeight);
- return size;
- }
-
- gfx::Image image_;
- int image_index_;
-
- DISALLOW_COPY_AND_ASSIGN(VolumeButton);
-};
-
-class MuteButton : public TrayBarButtonWithTitle {
- public:
- explicit MuteButton(views::ButtonListener* listener)
- : TrayBarButtonWithTitle(listener,
- -1, // no title under mute button
- kTrayBarButtonWidth) {
- Update();
- }
- virtual ~MuteButton() {}
-
- void Update() {
- UpdateButton(IsAudioMuted());
- SchedulePaint();
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MuteButton);
-};
-
-class VolumeSlider : public views::Slider {
- public:
- explicit VolumeSlider(views::SliderListener* listener)
- : views::Slider(listener, views::Slider::HORIZONTAL) {
- set_focus_border_color(kFocusBorderColor);
- SetValue(GetVolumeLevel());
- SetAccessibleName(
- ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
- IDS_ASH_STATUS_TRAY_VOLUME));
- Update();
- }
- virtual ~VolumeSlider() {}
-
- void Update() {
- UpdateState(!IsAudioMuted());
- }
-
- DISALLOW_COPY_AND_ASSIGN(VolumeSlider);
-};
-
-class VolumeView : public views::View,
- public views::ButtonListener,
- public views::SliderListener {
- public:
- VolumeView() {
- SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal,
- kTrayPopupPaddingHorizontal, 0, kTrayPopupPaddingBetweenItems));
-
- icon_ = new VolumeButton(this);
- AddChildView(icon_);
-
- mute_ = new MuteButton(this);
- AddChildView(mute_);
-
- slider_ = new VolumeSlider(this);
- AddChildView(slider_);
- }
-
- virtual ~VolumeView() {}
-
- void Update() {
- icon_->Update();
- mute_->Update();
- slider_->Update();
- }
-
- void SetVolumeLevel(float percent) {
- // The change in volume will be reflected via accessibility system events,
- // so we prevent the UI event from being sent here.
- slider_->set_enable_accessibility_events(false);
- slider_->SetValue(percent);
- // It is possible that the volume was (un)muted, but the actual volume level
- // did not change. In that case, setting the value of the slider won't
- // trigger an update. So explicitly trigger an update.
- Update();
- slider_->set_enable_accessibility_events(true);
- }
-
- private:
- // Overridden from views::View.
- virtual void OnBoundsChanged(const gfx::Rect& old_bounds) OVERRIDE {
- int w = width() - slider_->x();
- slider_->SetSize(gfx::Size(w, slider_->height()));
- }
-
- // Overridden from views::ButtonListener.
- virtual void ButtonPressed(views::Button* sender,
- const ui::Event& event) OVERRIDE {
- CHECK(sender == icon_ || sender == mute_);
- ash::Shell::GetInstance()->system_tray_delegate()->
- GetVolumeControlDelegate()->SetAudioMuted(!IsAudioMuted());
- }
-
- // Overridden from views:SliderListener.
- virtual void SliderValueChanged(views::Slider* sender,
- float value,
- float old_value,
- views::SliderChangeReason reason) OVERRIDE {
- if (reason == views::VALUE_CHANGED_BY_USER) {
- ash::Shell::GetInstance()->system_tray_delegate()->
- GetVolumeControlDelegate()->SetVolumeLevel(value);
- }
- icon_->Update();
- }
-
- VolumeButton* icon_;
- MuteButton* mute_;
- VolumeSlider* slider_;
-
- DISALLOW_COPY_AND_ASSIGN(VolumeView);
-};
-
-} // namespace tray
-
-TrayVolume::TrayVolume(SystemTray* system_tray)
- : TrayImageItem(system_tray, IDR_AURA_UBER_TRAY_VOLUME_MUTE),
- volume_view_(NULL),
- is_default_view_(false) {
- Shell::GetInstance()->system_tray_notifier()->AddAudioObserver(this);
-}
-
-TrayVolume::~TrayVolume() {
- Shell::GetInstance()->system_tray_notifier()->RemoveAudioObserver(this);
-}
-
-bool TrayVolume::GetInitialVisibility() {
- return IsAudioMuted();
-}
-
-views::View* TrayVolume::CreateDefaultView(user::LoginStatus status) {
- volume_view_ = new tray::VolumeView;
- is_default_view_ = true;
- return volume_view_;
-}
-
-views::View* TrayVolume::CreateDetailedView(user::LoginStatus status) {
- volume_view_ = new tray::VolumeView;
- is_default_view_ = false;
- return volume_view_;
-}
-
-void TrayVolume::DestroyDefaultView() {
- if (is_default_view_)
- volume_view_ = NULL;
-}
-
-void TrayVolume::DestroyDetailedView() {
- if (!is_default_view_)
- volume_view_ = NULL;
-}
-
-bool TrayVolume::ShouldHideArrow() const {
- return true;
-}
-
-bool TrayVolume::ShouldShowLauncher() const {
- return false;
-}
-
-void TrayVolume::OnVolumeChanged(float percent) {
- if (tray_view())
- tray_view()->SetVisible(GetInitialVisibility());
-
- if (volume_view_) {
- if (IsAudioMuted())
- percent = 0.0;
- volume_view_->SetVolumeLevel(percent);
- SetDetailedViewCloseDelay(kTrayPopupAutoCloseDelayInSeconds);
- return;
- }
- PopupDetailedView(kTrayPopupAutoCloseDelayInSeconds, false);
-}
-
-void TrayVolume::OnMuteToggled() {
- if (tray_view())
- tray_view()->SetVisible(GetInitialVisibility());
-
- if (volume_view_)
- volume_view_->Update();
- else
- PopupDetailedView(kTrayPopupAutoCloseDelayInSeconds, false);
-}
-
-} // namespace internal
-} // namespace ash
diff --git a/ash/system/tray/system_tray.cc b/ash/system/tray/system_tray.cc
index 2418f80..198d847 100644
--- a/ash/system/tray/system_tray.cc
+++ b/ash/system/tray/system_tray.cc
@@ -50,7 +50,7 @@
#include "ui/views/view.h"
#if defined(OS_CHROMEOS)
-#include "ash/system/chromeos/audio/tray_volume.h"
+#include "ash/system/chromeos/audio/tray_audio.h"
#include "ash/system/chromeos/enterprise/tray_enterprise.h"
#include "ash/system/chromeos/network/tray_network.h"
#include "ash/system/chromeos/network/tray_sms.h"
@@ -162,7 +162,7 @@ void SystemTray::CreateItems(SystemTrayDelegate* delegate) {
#if defined(OS_CHROMEOS)
AddTrayItem(new internal::TrayDisplay(this));
AddTrayItem(new internal::TrayScreenCapture(this));
- AddTrayItem(new internal::TrayVolume(this));
+ AddTrayItem(new internal::TrayAudio(this));
#endif
#if !defined(OS_WIN)
AddTrayItem(new internal::TrayBrightness(this));
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index e86ae00..eea2baf 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -6780,6 +6780,12 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_FLAGS_ASH_DISABLE_NEW_NETWORK_STATUS_AREA_DESCRIPTION" desc="Title for the flag to enable using the new Network State Handler.">
Disables the new network handlers which handle Shill communication without using NetworkLibrary for the status area.
</message>
+ <message name="IDS_FLAGS_ASH_ENABLE_NEW_AUDIO_HANDLER_NAME" desc="Title for the flag to enable using the new audio handler.">
+ Enables new audio handler
+ </message>
+ <message name="IDS_FLAGS_ASH_ENABLE_NEW_AUDIO_HANDLER_DESCRIPTION" desc="Description for the flag to enable using the new audio handler.">
+ Enable the new audio handler which uses the new cras audio dbus apis.
+ </message>
<message name="IDS_FLAGS_ENABLE_LOCALLY_MANAGED_USERS_NAME" desc="Title for the flag to enable locally managed users.">
Enable locally managed users
</message>
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 1e7902b..5a7f492 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1162,6 +1162,13 @@ const Experiment kExperiments[] = {
SINGLE_VALUE_TYPE(ash::switches::kAshDisableNewNetworkStatusArea),
},
{
+ "ash-enable-new-audio-handler",
+ IDS_FLAGS_ASH_ENABLE_NEW_AUDIO_HANDLER_NAME,
+ IDS_FLAGS_ASH_ENABLE_NEW_AUDIO_HANDLER_DESCRIPTION,
+ kOsCrOS,
+ SINGLE_VALUE_TYPE(ash::switches::kAshEnableNewAudioHandler)
+ },
+ {
"enable-carrier-switching",
IDS_FLAGS_ENABLE_CARRIER_SWITCHING,
IDS_FLAGS_ENABLE_CARRIER_SWITCHING_DESCRIPTION,
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index 6617ce3..6cc01e0 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -7,6 +7,7 @@
#include <string>
#include <vector>
+#include "ash/ash_switches.h"
#include "ash/shell.h"
#include "base/bind.h"
#include "base/callback.h"
@@ -90,6 +91,7 @@
#include "chrome/common/logging_chrome.h"
#include "chrome/common/pref_names.h"
#include "chromeos/audio/audio_pref_handler.h"
+#include "chromeos/audio/cras_audio_handler.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/cryptohome/async_method_caller.h"
#include "chromeos/dbus/dbus_thread_manager.h"
@@ -464,8 +466,14 @@ void ChromeBrowserMainPartsChromeos::PostMainMessageLoopStart() {
// Threads are initialized between MainMessageLoopStart and MainMessageLoopRun.
// about_flags settings are applied in ChromeBrowserMainParts::PreCreateThreads.
void ChromeBrowserMainPartsChromeos::PreMainMessageLoopRun() {
- AudioHandler::Initialize(
- AudioPrefHandler::Create(g_browser_process->local_state()));
+ if (CommandLine::ForCurrentProcess()->
+ HasSwitch(ash::switches::kAshEnableNewAudioHandler)) {
+ CrasAudioHandler::Initialize(
+ AudioPrefHandler::Create(g_browser_process->local_state()));
+ } else {
+ AudioHandler::Initialize(
+ AudioPrefHandler::Create(g_browser_process->local_state()));
+ }
base::FilePath downloads_directory;
CHECK(PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &downloads_directory));
@@ -776,7 +784,12 @@ void ChromeBrowserMainPartsChromeos::PostMainMessageLoopRun() {
// even if Initialize() wasn't called.
SystemKeyEventListener::Shutdown();
imageburner::BurnManager::Shutdown();
- AudioHandler::Shutdown();
+ if (CommandLine::ForCurrentProcess()->
+ HasSwitch(ash::switches::kAshEnableNewAudioHandler)) {
+ CrasAudioHandler::Shutdown();
+ } else {
+ AudioHandler::Shutdown();
+ }
WebSocketProxyController::Shutdown();
diff --git a/chrome/browser/chromeos/login/chrome_restart_request.cc b/chrome/browser/chromeos/login/chrome_restart_request.cc
index 7db0062..9279f5e 100644
--- a/chrome/browser/chromeos/login/chrome_restart_request.cc
+++ b/chrome/browser/chromeos/login/chrome_restart_request.cc
@@ -124,6 +124,7 @@ std::string DeriveCommandLine(const GURL& start_url,
ash::switches::kAshTouchHud,
ash::switches::kAuraLegacyPowerButton,
ash::switches::kAshDisableNewNetworkStatusArea,
+ ash::switches::kAshEnableNewAudioHandler,
// Please keep these in alphabetical order. Non-UI Compositor switches
// here should also be added to
// content/browser/renderer_host/render_process_host_impl.cc.
diff --git a/chrome/browser/chromeos/system/ash_system_tray_delegate.cc b/chrome/browser/chromeos/system/ash_system_tray_delegate.cc
index 1f64fcc..b8d187b 100644
--- a/chrome/browser/chromeos/system/ash_system_tray_delegate.cc
+++ b/chrome/browser/chromeos/system/ash_system_tray_delegate.cc
@@ -272,7 +272,10 @@ class SystemTrayDelegate : public ash::SystemTrayDelegate,
}
virtual void Initialize() OVERRIDE {
- AudioHandler::GetInstance()->AddVolumeObserver(this);
+ if (!CommandLine::ForCurrentProcess()->
+ HasSwitch(ash::switches::kAshEnableNewAudioHandler)) {
+ AudioHandler::GetInstance()->AddVolumeObserver(this);
+ }
DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
DBusThreadManager::Get()->GetPowerManagerClient()->RequestStatusUpdate(
PowerManagerClient::UPDATE_INITIAL);
@@ -333,9 +336,12 @@ class SystemTrayDelegate : public ash::SystemTrayDelegate,
}
virtual ~SystemTrayDelegate() {
- AudioHandler* audiohandler = AudioHandler::GetInstance();
- if (audiohandler)
- audiohandler->RemoveVolumeObserver(this);
+ if (!CommandLine::ForCurrentProcess()->
+ HasSwitch(ash::switches::kAshEnableNewAudioHandler) &&
+ AudioHandler::GetInstance()) {
+ AudioHandler::GetInstance()->RemoveVolumeObserver(this);
+ }
+
DBusThreadManager::Get()->GetSessionManagerClient()->RemoveObserver(this);
DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
DBusThreadManager::Get()->GetSystemClockClient()->RemoveObserver(this);
diff --git a/chrome/browser/ui/ash/volume_controller_chromeos.cc b/chrome/browser/ui/ash/volume_controller_chromeos.cc
index d9ba52d..5450b55 100644
--- a/chrome/browser/ui/ash/volume_controller_chromeos.cc
+++ b/chrome/browser/ui/ash/volume_controller_chromeos.cc
@@ -4,6 +4,8 @@
#include "chrome/browser/ui/ash/volume_controller_chromeos.h"
+#include "ash/ash_switches.h"
+#include "base/command_line.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/audio/audio_handler.h"
#include "chrome/browser/extensions/api/system_private/system_private_api.h"
@@ -16,10 +18,25 @@ const double kStepPercentage = 4.0;
} // namespace
+VolumeController::VolumeController() {
+ if (UseNewAudioHandler())
+ chromeos::CrasAudioHandler::Get()->AddAudioObserver(this);
+}
+
+VolumeController::~VolumeController() {
+ if (UseNewAudioHandler() && chromeos::CrasAudioHandler::IsInitialized())
+ chromeos::CrasAudioHandler::Get()->RemoveAudioObserver(this);
+}
+
bool VolumeController::HandleVolumeMute(const ui::Accelerator& accelerator) {
if (accelerator.key_code() == ui::VKEY_VOLUME_MUTE)
content::RecordAction(content::UserMetricsAction("Accel_VolumeMute_F8"));
+ if (UseNewAudioHandler()) {
+ chromeos::CrasAudioHandler::Get()->SetOutputMute(true);
+ return true;
+ }
+
chromeos::AudioHandler* audio_handler = chromeos::AudioHandler::GetInstance();
// Always muting (and not toggling) as per final decision on
@@ -35,6 +52,16 @@ bool VolumeController::HandleVolumeDown(const ui::Accelerator& accelerator) {
if (accelerator.key_code() == ui::VKEY_VOLUME_DOWN)
content::RecordAction(content::UserMetricsAction("Accel_VolumeDown_F9"));
+ if (UseNewAudioHandler()) {
+ chromeos::CrasAudioHandler* audio_handler =
+ chromeos::CrasAudioHandler::Get();
+ if (audio_handler->IsOutputMuted())
+ audio_handler->SetOutputVolumePercent(0);
+ else
+ audio_handler->AdjustOutputVolumeByPercent(-kStepPercentage);
+ return true;
+ }
+
chromeos::AudioHandler* audio_handler = chromeos::AudioHandler::GetInstance();
if (audio_handler->IsMuted())
audio_handler->SetVolumePercent(0.0);
@@ -50,6 +77,16 @@ bool VolumeController::HandleVolumeUp(const ui::Accelerator& accelerator) {
if (accelerator.key_code() == ui::VKEY_VOLUME_UP)
content::RecordAction(content::UserMetricsAction("Accel_VolumeUp_F10"));
+ if (UseNewAudioHandler()) {
+ chromeos::CrasAudioHandler* audio_handler =
+ chromeos::CrasAudioHandler::Get();
+ if (audio_handler->IsOutputMuted())
+ audio_handler->SetOutputMute(false);
+ else
+ audio_handler->AdjustOutputVolumeByPercent(kStepPercentage);
+ return true;
+ }
+
chromeos::AudioHandler* audio_handler = chromeos::AudioHandler::GetInstance();
if (audio_handler->IsMuted()) {
audio_handler->SetMuted(false);
@@ -63,15 +100,18 @@ bool VolumeController::HandleVolumeUp(const ui::Accelerator& accelerator) {
}
bool VolumeController::IsAudioMuted() const {
+ DCHECK(!UseNewAudioHandler());
return chromeos::AudioHandler::GetInstance()->IsMuted();
}
void VolumeController::SetAudioMuted(bool muted) {
+ DCHECK(!UseNewAudioHandler());
chromeos::AudioHandler::GetInstance()->SetMuted(muted);
}
// Gets the volume level. The range is [0, 1.0].
float VolumeController::GetVolumeLevel() const {
+ DCHECK(!UseNewAudioHandler());
return chromeos::AudioHandler::GetInstance()->GetVolumePercent() / 100.f;
}
@@ -81,8 +121,30 @@ void VolumeController::SetVolumeLevel(float level) {
}
void VolumeController::SetVolumePercent(double percent) {
+ DCHECK(!UseNewAudioHandler());
chromeos::AudioHandler* audio_handler = chromeos::AudioHandler::GetInstance();
audio_handler->SetVolumePercent(percent);
extensions::DispatchVolumeChangedEvent(audio_handler->GetVolumePercent(),
audio_handler->IsMuted());
}
+
+void VolumeController::OnOutputVolumeChanged() {
+ DCHECK(UseNewAudioHandler());
+ chromeos::CrasAudioHandler* audio_handler = chromeos::CrasAudioHandler::Get();
+ extensions::DispatchVolumeChangedEvent(
+ audio_handler->GetOutputVolumePercent(),
+ audio_handler->IsOutputMuted());
+}
+
+void VolumeController::OnOutputMuteChanged() {
+ DCHECK(UseNewAudioHandler());
+ chromeos::CrasAudioHandler* audio_handler = chromeos::CrasAudioHandler::Get();
+ extensions::DispatchVolumeChangedEvent(
+ audio_handler->GetOutputVolumePercent(),
+ audio_handler->IsOutputMuted());
+}
+
+bool VolumeController::UseNewAudioHandler() const {
+ return CommandLine::ForCurrentProcess()->
+ HasSwitch(ash::switches::kAshEnableNewAudioHandler);
+}
diff --git a/chrome/browser/ui/ash/volume_controller_chromeos.h b/chrome/browser/ui/ash/volume_controller_chromeos.h
index 925d3d3..d3b2005 100644
--- a/chrome/browser/ui/ash/volume_controller_chromeos.h
+++ b/chrome/browser/ui/ash/volume_controller_chromeos.h
@@ -8,13 +8,15 @@
#include "ash/volume_control_delegate.h"
#include "base/basictypes.h"
#include "base/compiler_specific.h"
+#include "chromeos/audio/cras_audio_handler.h"
// A class which controls volume when F8-10 or a multimedia key for volume is
// pressed.
-class VolumeController : public ash::VolumeControlDelegate {
+class VolumeController : public ash::VolumeControlDelegate,
+ public chromeos::CrasAudioHandler::AudioObserver {
public:
- VolumeController() {}
- virtual ~VolumeController() {}
+ VolumeController();
+ virtual ~VolumeController();
// Overridden from ash::VolumeControlDelegate:
virtual bool HandleVolumeMute(const ui::Accelerator& accelerator) OVERRIDE;
@@ -26,7 +28,13 @@ class VolumeController : public ash::VolumeControlDelegate {
virtual void SetVolumeLevel(float level) OVERRIDE;
virtual void SetVolumePercent(double percent) OVERRIDE;
+ // Overridden from chromeos::CrasAudioHandler::AudioObserver.
+ virtual void OnOutputVolumeChanged() OVERRIDE;
+ virtual void OnOutputMuteChanged() OVERRIDE;
+
private:
+ bool UseNewAudioHandler() const;
+
DISALLOW_COPY_AND_ASSIGN(VolumeController);
};
diff --git a/chromeos/audio/audio_device.cc b/chromeos/audio/audio_device.cc
new file mode 100644
index 0000000..f41acda
--- /dev/null
+++ b/chromeos/audio/audio_device.cc
@@ -0,0 +1,85 @@
+// Copyright (c) 2013 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 "chromeos/audio/audio_device.h"
+
+#include "base/format_macros.h"
+#include "base/stringprintf.h"
+#include "base/utf_string_conversions.h"
+
+namespace {
+
+std::string GetTypeString(chromeos::AudioDeviceType type) {
+ if (type == chromeos::AUDIO_TYPE_INTERNAL)
+ return "INTERNAL";
+ else if (type == chromeos::AUDIO_TYPE_HEADPHONE)
+ return "HEADPHONE";
+ else if (type == chromeos::AUDIO_TYPE_USB)
+ return "USB";
+ else if (type == chromeos::AUDIO_TYPE_BLUETOOTH)
+ return "BLUETOOTH";
+ else if (type == chromeos::AUDIO_TYPE_HDMI)
+ return "HDMI";
+ else
+ return "OTHER";
+}
+
+chromeos::AudioDeviceType GetAudioType(const std::string& node_type) {
+ if (node_type.find("INTERNAL_") != std::string::npos)
+ return chromeos::AUDIO_TYPE_INTERNAL;
+ else if (node_type.find("HEADPHONE") != std::string::npos)
+ return chromeos::AUDIO_TYPE_HEADPHONE;
+ else if (node_type.find("USB") != std::string::npos)
+ return chromeos::AUDIO_TYPE_USB;
+ else if (node_type.find("BLUETOOTH") != std::string::npos)
+ return chromeos::AUDIO_TYPE_BLUETOOTH;
+ else if (node_type.find("HDMI") != std::string::npos)
+ return chromeos::AUDIO_TYPE_HDMI;
+ else
+ return chromeos::AUDIO_TYPE_OTHER;
+}
+
+} // namespace
+
+namespace chromeos {
+
+AudioDevice::AudioDevice()
+ : is_input(false),
+ id(0),
+ active(false) {
+}
+
+AudioDevice::AudioDevice(const AudioNode& node) {
+ is_input = node.is_input;
+ id = node.id;
+ type = GetAudioType(node.type);
+ if (!node.name.empty() && node.name != "(default)")
+ display_name = UTF8ToUTF16(node.name);
+ else
+ display_name = UTF8ToUTF16(node.device_name);
+ active = node.active;
+}
+
+std::string AudioDevice::ToString() const {
+ std::string result;
+ base::StringAppendF(&result,
+ "is_input = %s ",
+ is_input ? "true" : "false");
+ base::StringAppendF(&result,
+ "id = %"PRIu64" ",
+ id);
+ base::StringAppendF(&result,
+ "display_name = %s ",
+ UTF16ToUTF8(display_name).c_str());
+ base::StringAppendF(&result,
+ "type = %s ",
+ GetTypeString(type).c_str());
+ base::StringAppendF(&result,
+ "active = %s ",
+ active ? "true" : "false");
+
+ return result;
+}
+
+} // namespace chromeos
diff --git a/chromeos/audio/audio_device.h b/chromeos/audio/audio_device.h
new file mode 100644
index 0000000..891f030
--- /dev/null
+++ b/chromeos/audio/audio_device.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2013 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 CHROMEOS_AUDIO_AUDIO_DEVICE_H_
+#define CHROMEOS_AUDIO_AUDIO_DEVICE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/string16.h"
+#include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/audio_node.h"
+
+namespace chromeos {
+
+enum AudioDeviceType {
+ AUDIO_TYPE_INTERNAL,
+ AUDIO_TYPE_HEADPHONE,
+ AUDIO_TYPE_USB,
+ AUDIO_TYPE_BLUETOOTH,
+ AUDIO_TYPE_HDMI,
+ AUDIO_TYPE_OTHER,
+};
+
+struct CHROMEOS_EXPORT AudioDevice {
+ bool is_input;
+ uint64 id;
+ base::string16 display_name;
+ AudioDeviceType type;
+ bool active;
+
+ AudioDevice();
+ explicit AudioDevice(const AudioNode& node);
+ std::string ToString() const;
+};
+
+typedef std::vector<AudioDevice> AudioDeviceList;
+
+} // namespace chromeos
+
+#endif // CHROMEOS_AUDIO_AUDIO_DEVICE_H_
diff --git a/chromeos/audio/cras_audio_handler.cc b/chromeos/audio/cras_audio_handler.cc
index e5bcec7..9666cc1 100644
--- a/chromeos/audio/cras_audio_handler.cc
+++ b/chromeos/audio/cras_audio_handler.cc
@@ -109,15 +109,31 @@ uint64 CrasAudioHandler::GetActiveInputNode() const {
return active_input_node_id_;
}
+void CrasAudioHandler::GetAudioDevices(AudioDeviceList* device_list) const {
+ for (size_t i = 0; i < audio_devices_.size(); ++i)
+ device_list->push_back(audio_devices_[i]);
+}
+
+bool CrasAudioHandler::GetActiveOutputDevice(AudioDevice* device) const {
+ for (size_t i = 0; i < audio_devices_.size(); ++i) {
+ if (audio_devices_[i].id == active_output_node_id_) {
+ *device = audio_devices_[i];
+ return true;
+ }
+ }
+ NOTREACHED() << "Can't find active output audio device";
+ return false;
+}
+
void CrasAudioHandler::SetOutputVolumePercent(int volume_percent) {
volume_percent = min(max(volume_percent, 0), 100);
if (volume_percent <= kMuteThresholdPercent)
volume_percent = 0;
+ SetOutputVolumeInternal(volume_percent);
if (IsOutputMuted() && volume_percent > 0)
SetOutputMute(false);
if (!IsOutputMuted() && volume_percent == 0)
SetOutputMute(true);
- SetOutputVolumeInternal(volume_percent);
}
void CrasAudioHandler::AdjustOutputVolumeByPercent(int adjust_by_percent) {
@@ -171,6 +187,8 @@ CrasAudioHandler::CrasAudioHandler(
output_volume_(0),
active_output_node_id_(0),
active_input_node_id_(0),
+ has_alternative_input_(false),
+ has_alternative_output_(false),
output_mute_locked_(false),
input_mute_locked_(false) {
chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->AddObserver(this);
@@ -222,12 +240,20 @@ void CrasAudioHandler::NodesChanged() {
}
void CrasAudioHandler::ActiveOutputNodeChanged(uint64 node_id) {
+ if (active_output_node_id_ == node_id)
+ return;
+
active_output_node_id_ = node_id;
+ GetNodes();
FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveOutputNodeChanged());
}
void CrasAudioHandler::ActiveInputNodeChanged(uint64 node_id) {
+ if (active_input_node_id_ == node_id)
+ return;
+
active_input_node_id_ = node_id;
+ GetNodes();
FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveInputNodeChanged());
}
@@ -241,8 +267,8 @@ void CrasAudioHandler::SetupInitialAudioState() {
// Set the initial audio state to the ones read from audio prefs.
output_mute_on_ = audio_pref_handler_->GetOutputMuteValue();
output_volume_ = audio_pref_handler_->GetOutputVolumeValue();
- SetOutputMute(output_mute_on_);
SetOutputVolumeInternal(output_volume_);
+ SetOutputMute(output_mute_on_);
// Get the initial audio data.
GetNodes();
@@ -282,13 +308,28 @@ void CrasAudioHandler::HandleGetNodes(const chromeos::AudioNodeList& node_list,
return;
}
- audio_nodes_.clear();
+ audio_devices_.clear();
+ active_input_node_id_ = 0;
+ active_output_node_id_ = 0;
+ has_alternative_input_ = false;
+ has_alternative_output_ = false;
+
for (size_t i = 0; i < node_list.size(); ++i) {
if (node_list[i].is_input && node_list[i].active)
active_input_node_id_ = node_list[i].id;
else if (!node_list[i].is_input && node_list[i].active)
active_output_node_id_ = node_list[i].id;
- audio_nodes_.push_back(node_list[i]);
+ AudioDevice device(node_list[i]);
+ audio_devices_.push_back(device);
+ if (!has_alternative_input_ &&
+ device.is_input &&
+ device.type != AUDIO_TYPE_INTERNAL) {
+ has_alternative_input_ = true;
+ } else if (!has_alternative_output_ &&
+ !device.is_input &&
+ device.type != AUDIO_TYPE_INTERNAL) {
+ has_alternative_output_ = true;
+ }
}
FOR_EACH_OBSERVER(AudioObserver, observers_, OnAudioNodesChanged());
diff --git a/chromeos/audio/cras_audio_handler.h b/chromeos/audio/cras_audio_handler.h
index 7e5f47a..61b11f5d 100644
--- a/chromeos/audio/cras_audio_handler.h
+++ b/chromeos/audio/cras_audio_handler.h
@@ -9,6 +9,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
+#include "chromeos/audio/audio_device.h"
#include "chromeos/audio/audio_pref_observer.h"
#include "chromeos/dbus/audio_node.h"
#include "chromeos/dbus/cras_audio_client.h"
@@ -83,6 +84,15 @@ class CHROMEOS_EXPORT CrasAudioHandler : public CrasAudioClient::Observer,
// Returns the node_id of the active input node.
uint64 GetActiveInputNode() const;
+ // Gets the audio devices back in |device_list|.
+ void GetAudioDevices(AudioDeviceList* device_list) const;
+
+ bool GetActiveOutputDevice(AudioDevice* device) const;
+
+ // Whether there is alternative input/output audio device.
+ bool has_alternative_input() const { return has_alternative_input_; }
+ bool has_alternative_output() const { return has_alternative_output_; }
+
// Sets volume level from 0-100%. If less than kMuteThresholdPercent, then
// mutes the sound. If it was muted, and |volume_percent| is larger than
// the threshold, then the sound is unmuted.
@@ -141,13 +151,15 @@ class CHROMEOS_EXPORT CrasAudioHandler : public CrasAudioClient::Observer,
ObserverList<AudioObserver> observers_;
// Audio data and state.
- AudioNodeList audio_nodes_;
+ AudioDeviceList audio_devices_;
VolumeState volume_state_;
bool output_mute_on_;
bool input_mute_on_;
int output_volume_;
uint64 active_output_node_id_;
uint64 active_input_node_id_;
+ bool has_alternative_input_;
+ bool has_alternative_output_;
bool output_mute_locked_;
bool input_mute_locked_;
diff --git a/chromeos/chromeos.gyp b/chromeos/chromeos.gyp
index 66d0461..2192796 100644
--- a/chromeos/chromeos.gyp
+++ b/chromeos/chromeos.gyp
@@ -31,6 +31,8 @@
'CHROMEOS_IMPLEMENTATION',
],
'sources': [
+ 'audio/audio_device.cc',
+ 'audio/audio_device.h',
'audio/audio_pref_observer.h',
'audio/audio_pref_handler.h',
'audio/cras_audio_handler.cc',
diff --git a/chromeos/dbus/cras_audio_client.cc b/chromeos/dbus/cras_audio_client.cc
index 342ce59..02260bb 100644
--- a/chromeos/dbus/cras_audio_client.cc
+++ b/chromeos/dbus/cras_audio_client.cc
@@ -384,25 +384,54 @@ class CrasAudioClientStubImpl : public CrasAudioClient {
CrasAudioClientStubImpl() {
VLOG(1) << "CrasAudioClientStubImpl is created";
- // Fake audio nodes.
+ // Fake audio output nodes.
AudioNode node_1;
node_1.is_input = false;
node_1.id = 10001;
- node_1.device_name = "Fake Audio Output";
+ node_1.device_name = "Fake Speaker";
node_1.type = "INTERNAL_SPEAKER";
- node_1.name = "Internal Speaker";
- node_1.active = true;
+ node_1.name = "Speaker";
+ node_1.active = false;
+ node_list_.push_back(node_1);
AudioNode node_2;
- node_2.is_input = true;
+ node_2.is_input = false;
node_2.id = 10002;
- node_2.device_name = "Fake Audio Input";
- node_2.type = "INTERNAL_MIC";
- node_2.name = "Internal Mic";
+ node_2.device_name = "Fake Headphone";
+ node_2.type = "HEADPHONE";
+ node_2.name = "Headphone";
node_2.active = true;
-
- node_list_.push_back(node_1);
node_list_.push_back(node_2);
+ active_output_node_id_ = node_2.id;
+
+ AudioNode node_3;
+ node_3.is_input = false;
+ node_3.id = 10003;
+ node_3.device_name = "Fake Audio Output";
+ node_3.type = "BLUETOOTH";
+ node_3.name = "Bluetooth Headphone";
+ node_3.active = false;
+ node_list_.push_back(node_3);
+
+ // Fake audio input ndoes
+ AudioNode node_4;
+ node_4.is_input = true;
+ node_4.id = 10004;
+ node_4.device_name = "Fake Internal Mic";
+ node_4.type = "INTERNAL_MIC";
+ node_4.name = "Internal Mic";
+ node_4.active = false;
+ node_list_.push_back(node_4);
+
+ AudioNode node_5;
+ node_5.is_input = true;
+ node_5.id = 10005;
+ node_5.device_name = "Fake Internal Mic";
+ node_5.type = "USB";
+ node_5.name = "USB Mic";
+ node_5.active = true;
+ node_list_.push_back(node_5);
+ active_input_node_id_ = node_5.id;
}
virtual ~CrasAudioClientStubImpl() {
}
@@ -459,6 +488,15 @@ class CrasAudioClientStubImpl : public CrasAudioClient {
}
virtual void SetActiveOutputNode(uint64 node_id) OVERRIDE {
+ if (active_output_node_id_ == node_id)
+ return;
+
+ for (size_t i = 0; i < node_list_.size(); ++i) {
+ if (node_list_[i].id == active_output_node_id_)
+ node_list_[i].active = false;
+ else if (node_list_[i].id == node_id)
+ node_list_[i].active = true;
+ }
active_output_node_id_ = node_id;
FOR_EACH_OBSERVER(Observer,
observers_,
@@ -466,6 +504,15 @@ class CrasAudioClientStubImpl : public CrasAudioClient {
}
virtual void SetActiveInputNode(uint64 node_id) OVERRIDE {
+ if (active_input_node_id_ == node_id)
+ return;
+
+ for (size_t i = 0; i < node_list_.size(); ++i) {
+ if (node_list_[i].id == active_input_node_id_)
+ node_list_[i].active = false;
+ else if (node_list_[i].id == node_id)
+ node_list_[i].active = true;
+ }
active_input_node_id_ = node_id;
FOR_EACH_OBSERVER(Observer,
observers_,