summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorzturner@chromium.org <zturner@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-19 20:25:56 +0000
committerzturner@chromium.org <zturner@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-19 20:25:56 +0000
commit667a0084e28414688620c50b8c342ba3bdeb8456 (patch)
treeb35725e99614c70b0f9e26a80a3d81bed2b45351
parente25b34a0c9dc137eb2fce6802ac3c4962c159060 (diff)
downloadchromium_src-667a0084e28414688620c50b8c342ba3bdeb8456.zip
chromium_src-667a0084e28414688620c50b8c342ba3bdeb8456.tar.gz
chromium_src-667a0084e28414688620c50b8c342ba3bdeb8456.tar.bz2
Resubmit 'Refactor the TrayAudio code so that it can be used by other platforms.'
This resubmission fixes a memory leak that was causing ASAN bots to fail. Original code review: https://codereview.chromium.org/163953007 BUG=227247 R=jennyz@chromium.org TBR=jamescook, jennyz Review URL: https://codereview.chromium.org/165393013 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@252064 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--ash/ash.gyp14
-rw-r--r--ash/ash_switches.cc6
-rw-r--r--ash/ash_switches.h2
-rw-r--r--ash/system/audio/audio_observer.h32
-rw-r--r--ash/system/audio/tray_audio.cc143
-rw-r--r--ash/system/audio/tray_audio.h (renamed from ash/system/chromeos/audio/tray_audio.h)45
-rw-r--r--ash/system/audio/tray_audio_delegate.h48
-rw-r--r--ash/system/audio/volume_view.cc332
-rw-r--r--ash/system/audio/volume_view.h90
-rw-r--r--ash/system/chromeos/audio/audio_detailed_view.cc177
-rw-r--r--ash/system/chromeos/audio/audio_detailed_view.h66
-rw-r--r--ash/system/chromeos/audio/tray_audio.cc663
-rw-r--r--ash/system/chromeos/audio/tray_audio_chromeos.cc61
-rw-r--r--ash/system/chromeos/audio/tray_audio_chromeos.h42
-rw-r--r--ash/system/chromeos/audio/tray_audio_delegate_chromeos.cc65
-rw-r--r--ash/system/chromeos/audio/tray_audio_delegate_chromeos.h33
-rw-r--r--ash/system/tray/system_tray.cc5
-rw-r--r--ash/system/tray/system_tray_notifier.cc43
-rw-r--r--ash/system/tray/system_tray_notifier.h12
-rw-r--r--chrome/browser/ui/ash/system_tray_delegate_chromeos.cc35
-rw-r--r--chrome/browser/ui/ash/system_tray_delegate_chromeos.h11
-rw-r--r--chromeos/audio/cras_audio_handler.cc4
-rw-r--r--chromeos/audio/cras_audio_handler.h3
23 files changed, 1241 insertions, 691 deletions
diff --git a/ash/ash.gyp b/ash/ash.gyp
index a78d666..3b8d961 100644
--- a/ash/ash.gyp
+++ b/ash/ash.gyp
@@ -270,12 +270,22 @@
'sticky_keys/sticky_keys_controller.h',
'sticky_keys/sticky_keys_overlay.cc',
'sticky_keys/sticky_keys_overlay.h',
+ 'system/audio/audio_observer.h',
+ 'system/audio/tray_audio.cc',
+ 'system/audio/tray_audio.h',
+ 'system/audio/tray_audio_delegate.h',
+ 'system/audio/volume_view.cc',
+ 'system/audio/volume_view.h',
'system/bluetooth/bluetooth_observer.h',
'system/bluetooth/tray_bluetooth.cc',
'system/bluetooth/tray_bluetooth.h',
'system/brightness_control_delegate.h',
- 'system/chromeos/audio/tray_audio.cc',
- 'system/chromeos/audio/tray_audio.h',
+ 'system/chromeos/audio/audio_detailed_view.cc',
+ 'system/chromeos/audio/audio_detailed_view.h',
+ 'system/chromeos/audio/tray_audio_chromeos.cc',
+ 'system/chromeos/audio/tray_audio_chromeos.h',
+ 'system/chromeos/audio/tray_audio_delegate_chromeos.cc',
+ 'system/chromeos/audio/tray_audio_delegate_chromeos.h',
'system/chromeos/enterprise/enterprise_domain_observer.h',
'system/chromeos/brightness/brightness_controller_chromeos.cc',
'system/chromeos/brightness/brightness_controller_chromeos.h',
diff --git a/ash/ash_switches.cc b/ash/ash_switches.cc
index 1241344..735bda0 100644
--- a/ash/ash_switches.cc
+++ b/ash/ash_switches.cc
@@ -256,12 +256,16 @@ bool UseDockedWindows() {
return !CommandLine::ForCurrentProcess()->HasSwitch(kAshDisableDockedWindows);
}
-#if defined(OS_CHROMEOS)
bool ShowAudioDeviceMenu() {
+#if defined(OS_CHROMEOS)
return !CommandLine::ForCurrentProcess()->
HasSwitch(kAshDisableAudioDeviceMenu);
+#else
+ return false;
+#endif
}
+#if defined(OS_CHROMEOS)
bool UseUsbChargerNotification() {
return !CommandLine::ForCurrentProcess()->
HasSwitch(kAshDisableUsbChargerNotification);
diff --git a/ash/ash_switches.h b/ash/ash_switches.h
index 80181c9..bf19557 100644
--- a/ash/ash_switches.h
+++ b/ash/ash_switches.h
@@ -101,10 +101,10 @@ ASH_EXPORT bool UseOverviewMode();
// Returns true if docked windows feature is enabled.
ASH_EXPORT bool UseDockedWindows();
-#if defined(OS_CHROMEOS)
// Returns true if we should show the audio device switching UI.
ASH_EXPORT bool ShowAudioDeviceMenu();
+#if defined(OS_CHROMEOS)
// Returns true if a notification should appear when a low-power USB charger
// is connected.
ASH_EXPORT bool UseUsbChargerNotification();
diff --git a/ash/system/audio/audio_observer.h b/ash/system/audio/audio_observer.h
new file mode 100644
index 0000000..6023226
--- /dev/null
+++ b/ash/system/audio/audio_observer.h
@@ -0,0 +1,32 @@
+// Copyright 2014 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_AUDIO_OBSERVER_H_
+#define ASH_SYSTEM_AUDIO_AUDIO_OBSERVER_H_
+
+namespace ash {
+
+class AudioObserver {
+ public:
+ virtual ~AudioObserver() {}
+
+ // Called when output volume changed.
+ virtual void OnOutputVolumeChanged() = 0;
+
+ // Called when output mute state changed.
+ virtual void OnOutputMuteChanged() = 0;
+
+ // Called when audio nodes changed.
+ virtual void OnAudioNodesChanged() = 0;
+
+ // Called when active audio output node changed.
+ virtual void OnActiveOutputNodeChanged() = 0;
+
+ // Called when active audio input node changed.
+ virtual void OnActiveInputNodeChanged() = 0;
+};
+
+} // namespace ash
+
+#endif // ASH_SYSTEM_AUDIO_AUDIO_OBSERVER_H_
diff --git a/ash/system/audio/tray_audio.cc b/ash/system/audio/tray_audio.cc
new file mode 100644
index 0000000..1a35514
--- /dev/null
+++ b/ash/system/audio/tray_audio.cc
@@ -0,0 +1,143 @@
+// Copyright 2014 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/tray_audio.h"
+
+#include <cmath>
+
+#include "ash/ash_constants.h"
+#include "ash/ash_switches.h"
+#include "ash/metrics/user_metrics_recorder.h"
+#include "ash/shell.h"
+#include "ash/system/audio/tray_audio_delegate.h"
+#include "ash/system/audio/volume_view.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/strings/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/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/font_list.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 {
+
+TrayAudio::TrayAudio(SystemTray* system_tray,
+ scoped_ptr<system::TrayAudioDelegate> audio_delegate)
+ : TrayImageItem(system_tray, IDR_AURA_UBER_TRAY_VOLUME_MUTE),
+ audio_delegate_(audio_delegate.Pass()),
+ volume_view_(NULL),
+ pop_up_volume_view_(false) {
+ Shell::GetInstance()->system_tray_notifier()->AddAudioObserver(this);
+}
+
+TrayAudio::~TrayAudio() {
+ Shell::GetInstance()->system_tray_notifier()->RemoveAudioObserver(this);
+}
+
+bool TrayAudio::GetInitialVisibility() {
+ return audio_delegate_->IsOutputAudioMuted();
+}
+
+views::View* TrayAudio::CreateDefaultView(user::LoginStatus status) {
+ volume_view_ = new tray::VolumeView(this, audio_delegate_.get(), true);
+ return volume_view_;
+}
+
+views::View* TrayAudio::CreateDetailedView(user::LoginStatus status) {
+ volume_view_ = new tray::VolumeView(this, audio_delegate_.get(), false);
+ return volume_view_;
+}
+
+void TrayAudio::DestroyDefaultView() {
+ volume_view_ = NULL;
+}
+
+void TrayAudio::DestroyDetailedView() {
+ if (volume_view_) {
+ volume_view_ = NULL;
+ pop_up_volume_view_ = false;
+ }
+}
+
+bool TrayAudio::ShouldHideArrow() const {
+ return true;
+}
+
+bool TrayAudio::ShouldShowShelf() const {
+ return ash::switches::ShowAudioDeviceMenu() && !pop_up_volume_view_;
+}
+
+void TrayAudio::OnOutputVolumeChanged() {
+ float percent =
+ static_cast<float>(audio_delegate_->GetOutputVolumeLevel()) / 100.0f;
+ 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() {
+ if (tray_view())
+ tray_view()->SetVisible(GetInitialVisibility());
+
+ if (volume_view_) {
+ volume_view_->Update();
+ SetDetailedViewCloseDelay(kTrayPopupAutoCloseDelayInSeconds);
+ } 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 (tray_view())
+ tray_view()->SetVisible(GetInitialVisibility());
+ if (volume_view_) {
+ volume_view_->SetVolumeLevel(
+ static_cast<float>(audio_delegate_->GetOutputVolumeLevel()) / 100.0f);
+ volume_view_->Update();
+ }
+}
+
+} // namespace internal
+} // namespace ash
diff --git a/ash/system/chromeos/audio/tray_audio.h b/ash/system/audio/tray_audio.h
index eb6e117a..923245a 100644
--- a/ash/system/chromeos/audio/tray_audio.h
+++ b/ash/system/audio/tray_audio.h
@@ -1,27 +1,43 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 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_CHROMEOS_AUDIO_TRAY_AUDIO_H_
-#define ASH_SYSTEM_CHROMEOS_AUDIO_TRAY_AUDIO_H_
+#ifndef ASH_SYSTEM_AUDIO_TRAY_AUDIO_H_
+#define ASH_SYSTEM_AUDIO_TRAY_AUDIO_H_
+#include "ash/system/audio/audio_observer.h"
#include "ash/system/tray/tray_image_item.h"
-#include "chromeos/audio/cras_audio_handler.h"
+#include "base/memory/scoped_ptr.h"
namespace ash {
+
+namespace system {
+class TrayAudioDelegate;
+}
+
namespace internal {
namespace tray {
class VolumeView;
-class AudioDetailedView;
}
class TrayAudio : public TrayImageItem,
- public chromeos::CrasAudioHandler::AudioObserver {
+ public AudioObserver {
public:
- explicit TrayAudio(SystemTray* system_tray);
+ TrayAudio(SystemTray* system_tray,
+ scoped_ptr<system::TrayAudioDelegate> audio_delegate);
virtual ~TrayAudio();
+ protected:
+ virtual void Update();
+
+ scoped_ptr<system::TrayAudioDelegate> audio_delegate_;
+ tray::VolumeView* volume_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_;
+
private:
// Overridden from TrayImageItem.
virtual bool GetInitialVisibility() OVERRIDE;
@@ -34,28 +50,17 @@ class TrayAudio : public TrayImageItem,
virtual bool ShouldHideArrow() const OVERRIDE;
virtual bool ShouldShowShelf() const OVERRIDE;
- // Overridden from chromeos::CrasAudioHandler::AudioObserver.
+ // Overridden from AudioObserver.
virtual void OnOutputVolumeChanged() OVERRIDE;
virtual void OnOutputMuteChanged() OVERRIDE;
- virtual void OnInputGainChanged() OVERRIDE;
- virtual void OnInputMuteChanged() OVERRIDE;
virtual void OnAudioNodesChanged() OVERRIDE;
virtual void OnActiveOutputNodeChanged() OVERRIDE;
virtual void OnActiveInputNodeChanged() OVERRIDE;
- void Update();
-
- tray::VolumeView* volume_view_;
- tray::AudioDetailedView* audio_detail_;
-
- // 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(TrayAudio);
};
} // namespace internal
} // namespace ash
-#endif // ASH_SYSTEM_CHROMEOS_AUDIO_TRAY_AUDIO_H_
+#endif // ASH_SYSTEM_AUDIO_TRAY_AUDIO_H_
diff --git a/ash/system/audio/tray_audio_delegate.h b/ash/system/audio/tray_audio_delegate.h
new file mode 100644
index 0000000..823bfff
--- /dev/null
+++ b/ash/system/audio/tray_audio_delegate.h
@@ -0,0 +1,48 @@
+// Copyright 2014 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_TRAY_AUDIO_DELEGATE_H_
+#define ASH_SYSTEM_AUDIO_TRAY_AUDIO_DELEGATE_H_
+
+namespace ash {
+namespace system {
+
+class TrayAudioDelegate {
+ public:
+
+ enum { kNoAudioDeviceIcon = -1 };
+
+ virtual ~TrayAudioDelegate() {}
+
+ // Sets the volume level of the output device to the minimum level which is
+ // deemed to be audible.
+ virtual void AdjustOutputVolumeToAudibleLevel() = 0;
+
+ // Gets the default level in the range 0%-100% at which the output device
+ // should be muted.
+ virtual int GetOutputDefaultVolumeMuteLevel() = 0;
+
+ // Gets the icon to use for the active output device.
+ virtual int GetActiveOutputDeviceIconId() = 0;
+
+ // Returns the volume level of the output device in the range 0%-100%.
+ virtual int GetOutputVolumeLevel() = 0;
+
+ // Returns true if the device has alternative inputs or outputs.
+ virtual bool HasAlternativeSources() = 0;
+
+ // Returns whether the output volume is muted.
+ virtual bool IsOutputAudioMuted() = 0;
+
+ // Sets the mute state of the output device.
+ virtual void SetOutputAudioIsMuted(bool is_muted) = 0;
+
+ // Sets the volume level of the output device in the range 0%-100%
+ virtual void SetOutputVolumeLevel(int level) = 0;
+};
+
+} // namespace system
+} // namespace ash
+
+#endif // ASH_SYSTEM_AUDIO_TRAY_AUDIO_DELEGATE_H_
diff --git a/ash/system/audio/volume_view.cc b/ash/system/audio/volume_view.cc
new file mode 100644
index 0000000..5cb3970
--- /dev/null
+++ b/ash/system/audio/volume_view.cc
@@ -0,0 +1,332 @@
+// Copyright 2014 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/volume_view.h"
+
+#include "ash/ash_constants.h"
+#include "ash/ash_switches.h"
+#include "ash/shell.h"
+#include "ash/system/audio/tray_audio_delegate.h"
+#include "ash/system/tray/system_tray_item.h"
+#include "ash/system/tray/tray_constants.h"
+#include "grit/ash_resources.h"
+#include "grit/ash_strings.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/canvas.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/layout/box_layout.h"
+
+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;
+
+// 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;
+
+} // namespace
+
+namespace ash {
+namespace internal {
+namespace tray {
+
+class VolumeButton : public views::ToggleImageButton {
+ public:
+ VolumeButton(views::ButtonListener* listener,
+ system::TrayAudioDelegate* audio_delegate)
+ : views::ToggleImageButton(listener),
+ audio_delegate_(audio_delegate),
+ 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 =
+ static_cast<float>(audio_delegate_->GetOutputVolumeLevel()) / 100.0f;
+ int image_index = audio_delegate_->IsOutputAudioMuted() ?
+ 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;
+ }
+
+ system::TrayAudioDelegate* audio_delegate_;
+ gfx::Image image_;
+ int image_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(VolumeButton);
+};
+
+class VolumeSlider : public views::Slider {
+ public:
+ VolumeSlider(views::SliderListener* listener,
+ system::TrayAudioDelegate* audio_delegate)
+ : views::Slider(listener, views::Slider::HORIZONTAL),
+ audio_delegate_(audio_delegate) {
+ set_focus_border_color(kFocusBorderColor);
+ SetValue(
+ static_cast<float>(audio_delegate_->GetOutputVolumeLevel()) / 100.0f);
+ SetAccessibleName(
+ ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
+ IDS_ASH_STATUS_TRAY_VOLUME));
+ Update();
+ }
+ virtual ~VolumeSlider() {}
+
+ void Update() {
+ UpdateState(!audio_delegate_->IsOutputAudioMuted());
+ }
+
+ private:
+ system::TrayAudioDelegate* audio_delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(VolumeSlider);
+};
+
+// Vertical bar separator that can be placed on the VolumeView.
+class BarSeparator : public views::View {
+ public:
+ BarSeparator() {}
+ virtual ~BarSeparator() {}
+
+ // Overriden from views::View.
+ virtual gfx::Size GetPreferredSize() OVERRIDE {
+ return gfx::Size(kBarSeparatorWidth, kBarSeparatorHeight);
+ }
+
+ private:
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
+ canvas->FillRect(gfx::Rect(width() / 2, 0, 1, height()),
+ kButtonStrokeColor);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(BarSeparator);
+};
+
+VolumeView::VolumeView(SystemTrayItem* owner,
+ system::TrayAudioDelegate* audio_delegate,
+ bool is_default_view)
+ : owner_(owner),
+ audio_delegate_(audio_delegate),
+ icon_(NULL),
+ slider_(NULL),
+ bar_(NULL),
+ device_type_(NULL),
+ more_(NULL),
+ is_default_view_(is_default_view) {
+ SetFocusable(false);
+ SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal,
+ kTrayPopupPaddingHorizontal, 0, kTrayPopupPaddingBetweenItems));
+
+ icon_ = new VolumeButton(this, audio_delegate_);
+ AddChildView(icon_);
+
+ slider_ = new VolumeSlider(this, audio_delegate_);
+ AddChildView(slider_);
+
+ bar_ = new BarSeparator;
+ AddChildView(bar_);
+
+ device_type_ = new views::ImageView;
+ AddChildView(device_type_);
+
+ more_ = new views::ImageView;
+ more_->EnableCanvasFlippingForRTLUI(true);
+ more_->SetImage(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
+ IDR_AURA_UBER_TRAY_MORE).ToImageSkia());
+ AddChildView(more_);
+
+ Update();
+}
+
+VolumeView::~VolumeView() {
+}
+
+void VolumeView::Update() {
+ icon_->Update();
+ slider_->Update();
+ UpdateDeviceTypeAndMore();
+ Layout();
+}
+
+void VolumeView::SetVolumeLevel(float percent) {
+ // 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 1%.
+ if (std::abs(percent-slider_->value()) < 0.01)
+ return;
+ // 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);
+}
+
+void VolumeView::UpdateDeviceTypeAndMore() {
+ if (!ash::switches::ShowAudioDeviceMenu() || !is_default_view_) {
+ more_->SetVisible(false);
+ bar_->SetVisible(false);
+ device_type_->SetVisible(false);
+ return;
+ }
+
+ bool show_more = audio_delegate_->HasAlternativeSources();
+ more_->SetVisible(show_more);
+ bar_->SetVisible(show_more);
+
+ // Show output device icon if necessary.
+ int device_icon = audio_delegate_->GetActiveOutputDeviceIconId();
+ if (device_icon != system::TrayAudioDelegate::kNoAudioDeviceIcon) {
+ device_type_->SetVisible(true);
+ device_type_->SetImage(
+ ui::ResourceBundle::GetSharedInstance().GetImageNamed(
+ device_icon).ToImageSkia());
+ } else {
+ device_type_->SetVisible(false);
+ }
+}
+
+void VolumeView::HandleVolumeUp(float level) {
+ audio_delegate_->SetOutputVolumeLevel(level);
+ if (audio_delegate_->IsOutputAudioMuted() &&
+ level > audio_delegate_->GetOutputDefaultVolumeMuteLevel()) {
+ audio_delegate_->SetOutputAudioIsMuted(false);
+ }
+}
+
+void VolumeView::HandleVolumeDown(float level) {
+ audio_delegate_->SetOutputVolumeLevel(level);
+ if (!audio_delegate_->IsOutputAudioMuted() &&
+ level <= audio_delegate_->GetOutputDefaultVolumeMuteLevel()) {
+ audio_delegate_->SetOutputAudioIsMuted(true);
+ } else if (audio_delegate_->IsOutputAudioMuted() &&
+ level > audio_delegate_->GetOutputDefaultVolumeMuteLevel()) {
+ audio_delegate_->SetOutputAudioIsMuted(false);
+ }
+}
+
+void VolumeView::Layout() {
+ 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 either bar_ or device_type_ at the left of the more_ button.
+ views::View* view_left_to_more;
+ if (device_type_->visible())
+ view_left_to_more = device_type_;
+ else
+ view_left_to_more = bar_;
+ gfx::Size view_size = view_left_to_more->GetPreferredSize();
+ gfx::Rect view_bounds(view_size);
+ view_bounds.set_x(more_->bounds().x() - view_size.width() -
+ kExtraPaddingBetweenBarAndMore);
+ view_bounds.set_y((height() - view_size.height()) / 2);
+ view_left_to_more->SetBoundsRect(view_bounds);
+
+ // Layout vertical bar next to view_left_to_more if device_type_ is visible.
+ if (device_type_->visible()) {
+ gfx::Size bar_size = bar_->GetPreferredSize();
+ gfx::Rect bar_bounds(bar_size);
+ bar_bounds.set_x(view_left_to_more->bounds().x() - bar_size.width());
+ bar_bounds.set_y((height() - bar_size.height()) / 2);
+ bar_->SetBoundsRect(bar_bounds);
+ }
+
+ // Layout slider, calculate slider width.
+ gfx::Rect slider_bounds = slider_->bounds();
+ slider_bounds.set_width(
+ bar_->bounds().x()
+ - (device_type_->visible() ? 0 : kTrayPopupPaddingBetweenItems)
+ - slider_bounds.x());
+ slider_->SetBoundsRect(slider_bounds);
+}
+
+void VolumeView::ButtonPressed(views::Button* sender, const ui::Event& event) {
+ CHECK(sender == icon_);
+ bool mute_on = !audio_delegate_->IsOutputAudioMuted();
+ audio_delegate_->SetOutputAudioIsMuted(mute_on);
+ if (!mute_on)
+ audio_delegate_->AdjustOutputVolumeToAudibleLevel();
+ icon_->Update();
+}
+
+void VolumeView::SliderValueChanged(views::Slider* sender,
+ float value,
+ float old_value,
+ views::SliderChangeReason reason) {
+ if (reason == views::VALUE_CHANGED_BY_USER) {
+ float new_volume = value * 100.0f;
+ float current_volume = audio_delegate_->GetOutputVolumeLevel();
+ // Do not call change audio volume if the difference is less than
+ // 1%, which is beyond cras audio api's granularity for output volume.
+ if (std::abs(new_volume - current_volume) < 1.0f)
+ return;
+ Shell::GetInstance()->metrics()->RecordUserMetricsAction(
+ is_default_view_ ?
+ ash::UMA_STATUS_AREA_CHANGED_VOLUME_MENU :
+ ash::UMA_STATUS_AREA_CHANGED_VOLUME_POPUP);
+ if (new_volume > current_volume)
+ HandleVolumeUp(new_volume);
+ else
+ HandleVolumeDown(new_volume);
+ }
+ icon_->Update();
+}
+
+bool VolumeView::PerformAction(const ui::Event& event) {
+ if (!more_->visible())
+ return false;
+ owner_->TransitionDetailedView();
+ return true;
+}
+
+} // namespace tray
+} // namespace internal
+} // namespace ash
diff --git a/ash/system/audio/volume_view.h b/ash/system/audio/volume_view.h
new file mode 100644
index 0000000..a3e8291
--- /dev/null
+++ b/ash/system/audio/volume_view.h
@@ -0,0 +1,90 @@
+// Copyright 2014 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_VOLUME_VIEW_H_
+#define ASH_SYSTEM_AUDIO_VOLUME_VIEW_H_
+
+#include "ash/system/tray/actionable_view.h"
+#include "ui/gfx/font.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/controls/slider.h"
+
+namespace views {
+class View;
+class ImageView;
+}
+
+namespace ash {
+
+class SystemTrayItem;
+
+namespace system {
+class TrayAudioDelegate;
+}
+
+namespace internal {
+
+class HoverHighlightView;
+
+namespace tray {
+
+class BarSeparator;
+class VolumeButton;
+class VolumeSlider;
+
+class VolumeView : public ActionableView,
+ public views::ButtonListener,
+ public views::SliderListener {
+ public:
+ VolumeView(SystemTrayItem* owner,
+ system::TrayAudioDelegate* audio_delegate,
+ bool is_default_view);
+
+ virtual ~VolumeView();
+
+ void Update();
+
+ // Sets volume level on slider_, |percent| is ranged from [0.00] to [1.00].
+ void SetVolumeLevel(float percent);
+
+ private:
+ // Updates bar_, device_type_ icon, and more_ buttons.
+ void UpdateDeviceTypeAndMore();
+ void HandleVolumeUp(float percent);
+ void HandleVolumeDown(float percent);
+
+ // Overridden from views::View.
+ virtual void Layout() OVERRIDE;
+
+ // Overridden from views::ButtonListener.
+ virtual void ButtonPressed(views::Button* sender,
+ const ui::Event& event) OVERRIDE;
+
+ // Overridden from views::SliderListener.
+ virtual void SliderValueChanged(views::Slider* sender,
+ float value,
+ float old_value,
+ views::SliderChangeReason reason) OVERRIDE;
+
+ // Overriden from ActionableView.
+ virtual bool PerformAction(const ui::Event& event) OVERRIDE;
+
+ SystemTrayItem* owner_;
+ system::TrayAudioDelegate* audio_delegate_;
+ VolumeButton* icon_;
+ VolumeSlider* slider_;
+ BarSeparator* bar_;
+ views::ImageView* device_type_;
+ views::ImageView* more_;
+ bool is_default_view_;
+
+ DISALLOW_COPY_AND_ASSIGN(VolumeView);
+};
+
+} // namespace tray
+} // namespace internal
+} // namespace ash
+
+#endif // ASH_SYSTEM_AUDIO_VOLUME_VIEW_H_
+
diff --git a/ash/system/chromeos/audio/audio_detailed_view.cc b/ash/system/chromeos/audio/audio_detailed_view.cc
new file mode 100644
index 0000000..803f4dd
--- /dev/null
+++ b/ash/system/chromeos/audio/audio_detailed_view.cc
@@ -0,0 +1,177 @@
+// Copyright 2014 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/audio_detailed_view.h"
+
+#include "ash/system/tray/fixed_sized_scroll_view.h"
+#include "ash/system/tray/hover_highlight_view.h"
+#include "ash/system/tray/tray_constants.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chromeos/audio/cras_audio_handler.h"
+#include "grit/ash_resources.h"
+#include "grit/ash_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/views/border.h"
+#include "ui/views/controls/label.h"
+
+namespace {
+
+base::string16 GetAudioDeviceName(const chromeos::AudioDevice& device) {
+ switch(device.type) {
+ case chromeos::AUDIO_TYPE_HEADPHONE:
+ return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_HEADPHONE);
+ case chromeos::AUDIO_TYPE_INTERNAL_SPEAKER:
+ return l10n_util::GetStringUTF16(
+ IDS_ASH_STATUS_TRAY_AUDIO_INTERNAL_SPEAKER);
+ case chromeos::AUDIO_TYPE_INTERNAL_MIC:
+ return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_INTERNAL_MIC);
+ case chromeos::AUDIO_TYPE_USB:
+ return l10n_util::GetStringFUTF16(
+ IDS_ASH_STATUS_TRAY_AUDIO_USB_DEVICE,
+ base::UTF8ToUTF16(device.display_name));
+ case chromeos::AUDIO_TYPE_BLUETOOTH:
+ return l10n_util::GetStringFUTF16(
+ IDS_ASH_STATUS_TRAY_AUDIO_BLUETOOTH_DEVICE,
+ base::UTF8ToUTF16(device.display_name));
+ case chromeos::AUDIO_TYPE_HDMI:
+ return l10n_util::GetStringFUTF16(
+ IDS_ASH_STATUS_TRAY_AUDIO_HDMI_DEVICE,
+ base::UTF8ToUTF16(device.display_name));
+ default:
+ return base::UTF8ToUTF16(device.display_name);
+ }
+}
+
+} // namespace
+
+using chromeos::CrasAudioHandler;
+
+namespace ash {
+namespace internal {
+namespace tray {
+
+AudioDetailedView::AudioDetailedView(SystemTrayItem* owner,
+ user::LoginStatus login)
+ : TrayDetailsView(owner),
+ login_(login) {
+ CreateItems();
+ Update();
+}
+
+AudioDetailedView::~AudioDetailedView() {
+}
+
+void AudioDetailedView::Update() {
+ UpdateAudioDevices();
+ Layout();
+}
+
+void AudioDetailedView::AddScrollListInfoItem(const base::string16& text) {
+ views::Label* label = new views::Label(
+ text,
+ ui::ResourceBundle::GetSharedInstance().GetFontList(
+ ui::ResourceBundle::BoldFont));
+
+ // Align info item with checkbox items
+ int margin = kTrayPopupPaddingHorizontal +
+ kTrayPopupDetailsLabelExtraLeftMargin;
+ int left_margin = 0;
+ int right_margin = 0;
+ if (base::i18n::IsRTL())
+ right_margin = margin;
+ else
+ left_margin = margin;
+
+ label->SetBorder(views::Border::CreateEmptyBorder(
+ ash::kTrayPopupPaddingBetweenItems,
+ left_margin,
+ ash::kTrayPopupPaddingBetweenItems,
+ right_margin));
+ label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ label->SetEnabledColor(SkColorSetARGB(192, 0, 0, 0));
+
+ scroll_content()->AddChildView(label);
+}
+
+HoverHighlightView* AudioDetailedView::AddScrollListItem(
+ const base::string16& text,
+ gfx::Font::FontStyle style,
+ bool checked) {
+ HoverHighlightView* container = new HoverHighlightView(this);
+ container->AddCheckableLabel(text, style, checked);
+ scroll_content()->AddChildView(container);
+ return container;
+}
+
+void AudioDetailedView::CreateHeaderEntry() {
+ CreateSpecialRow(IDS_ASH_STATUS_TRAY_AUDIO, this);
+}
+
+void AudioDetailedView::CreateItems() {
+ CreateScrollableList();
+ CreateHeaderEntry();
+}
+
+void AudioDetailedView::UpdateAudioDevices() {
+ output_devices_.clear();
+ input_devices_.clear();
+ chromeos::AudioDeviceList devices;
+ 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 AudioDetailedView::UpdateScrollableList() {
+ scroll_content()->RemoveAllChildViews(true);
+ device_map_.clear();
+
+ // Add audio output devices.
+ AddScrollListInfoItem(
+ l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_OUTPUT));
+ for (size_t i = 0; i < output_devices_.size(); ++i) {
+ HoverHighlightView* container = AddScrollListItem(
+ GetAudioDeviceName(output_devices_[i]),
+ gfx::Font::NORMAL,
+ output_devices_[i].active); /* checkmark if active */
+ device_map_[container] = output_devices_[i];
+ }
+
+ AddScrollSeparator();
+
+ // Add audio input devices.
+ AddScrollListInfoItem(
+ l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_INPUT));
+ for (size_t i = 0; i < input_devices_.size(); ++i) {
+ HoverHighlightView* container = AddScrollListItem(
+ GetAudioDeviceName(input_devices_[i]),
+ gfx::Font::NORMAL,
+ input_devices_[i].active); /* checkmark if active */
+ device_map_[container] = input_devices_[i];
+ }
+
+ scroll_content()->SizeToPreferredSize();
+ scroller()->Layout();
+}
+
+void AudioDetailedView::OnViewClicked(views::View* sender) {
+ if (sender == footer()->content()) {
+ TransitionToDefaultView();
+ } else {
+ AudioDeviceMap::iterator iter = device_map_.find(sender);
+ if (iter == device_map_.end())
+ return;
+ chromeos::AudioDevice& device = iter->second;
+ CrasAudioHandler::Get()->SwitchToDevice(device);
+ }
+}
+
+} // namespace tray
+} // namespace internal
+} // namespace ash
diff --git a/ash/system/chromeos/audio/audio_detailed_view.h b/ash/system/chromeos/audio/audio_detailed_view.h
new file mode 100644
index 0000000..6953676
--- /dev/null
+++ b/ash/system/chromeos/audio/audio_detailed_view.h
@@ -0,0 +1,66 @@
+// Copyright 2014 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_CHROMEOS_AUDIO_AUDIO_DETAILED_VIEW_H_
+#define ASH_SYSTEM_CHROMEOS_AUDIO_AUDIO_DETAILED_VIEW_H_
+
+#include "ash/system/tray/tray_details_view.h"
+#include "ash/system/tray/view_click_listener.h"
+#include "ash/system/user/login_status.h"
+#include "chromeos/audio/audio_device.h"
+
+#include "ui/gfx/font.h"
+
+namespace views {
+class View;
+}
+
+namespace ash {
+namespace internal {
+
+class HoverHighlightView;
+
+namespace tray {
+
+class AudioDetailedView : public TrayDetailsView,
+ public ViewClickListener {
+ public:
+ AudioDetailedView(SystemTrayItem* owner, user::LoginStatus login);
+
+ virtual ~AudioDetailedView();
+
+ void Update();
+
+ private:
+ void AddScrollListInfoItem(const base::string16& text);
+
+ HoverHighlightView* AddScrollListItem(const base::string16& text,
+ gfx::Font::FontStyle style,
+ bool checked);
+
+ void CreateHeaderEntry();
+ void CreateItems();
+
+ void UpdateScrollableList();
+ void UpdateAudioDevices();
+
+ // Overridden from ViewClickListener.
+ virtual void OnViewClicked(views::View* sender) OVERRIDE;
+
+ user::LoginStatus login_;
+
+ typedef std::map<views::View*, chromeos::AudioDevice> AudioDeviceMap;
+
+ chromeos::AudioDeviceList output_devices_;
+ chromeos::AudioDeviceList input_devices_;
+ AudioDeviceMap device_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioDetailedView);
+};
+
+} // namespace tray
+} // namespace internal
+} // namespace ash
+
+#endif // ASH_SYSTEM_CHROMEOS_AUDIO_AUDIO_DETAILED_VIEW_H_
diff --git a/ash/system/chromeos/audio/tray_audio.cc b/ash/system/chromeos/audio/tray_audio.cc
deleted file mode 100644
index c1721f6..0000000
--- a/ash/system/chromeos/audio/tray_audio.cc
+++ /dev/null
@@ -1,663 +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_audio.h"
-
-#include <cmath>
-
-#include "ash/ash_constants.h"
-#include "ash/ash_switches.h"
-#include "ash/metrics/user_metrics_recorder.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/strings/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/font_list.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"
-
-using chromeos::CrasAudioHandler;
-
-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 IsAudioMuted() {
- return CrasAudioHandler::Get()->IsOutputMuted();
-}
-
-float GetVolumeLevel() {
- return CrasAudioHandler::Get()->GetOutputVolumePercent() / 100.0f;
-}
-
-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;
-}
-
-base::string16 GetAudioDeviceName(const chromeos::AudioDevice& device) {
- switch(device.type) {
- case chromeos::AUDIO_TYPE_HEADPHONE:
- return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_HEADPHONE);
- case chromeos::AUDIO_TYPE_INTERNAL_SPEAKER:
- return l10n_util::GetStringUTF16(
- IDS_ASH_STATUS_TRAY_AUDIO_INTERNAL_SPEAKER);
- case chromeos::AUDIO_TYPE_INTERNAL_MIC:
- return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_INTERNAL_MIC);
- case chromeos::AUDIO_TYPE_USB:
- return l10n_util::GetStringFUTF16(
- IDS_ASH_STATUS_TRAY_AUDIO_USB_DEVICE,
- base::UTF8ToUTF16(device.display_name));
- case chromeos::AUDIO_TYPE_BLUETOOTH:
- return l10n_util::GetStringFUTF16(
- IDS_ASH_STATUS_TRAY_AUDIO_BLUETOOTH_DEVICE,
- base::UTF8ToUTF16(device.display_name));
- case chromeos::AUDIO_TYPE_HDMI:
- return l10n_util::GetStringFUTF16(
- IDS_ASH_STATUS_TRAY_AUDIO_HDMI_DEVICE,
- base::UTF8ToUTF16(device.display_name));
- default:
- return base::UTF8ToUTF16(device.display_name);
- }
-}
-
-} // 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() {}
-
- // Overriden from views::View.
- virtual gfx::Size GetPreferredSize() OVERRIDE {
- return gfx::Size(kBarSeparatorWidth, kBarSeparatorHeight);
- }
-
- private:
- 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) {
- SetFocusable(false);
- SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal,
- kTrayPopupPaddingHorizontal, 0, kTrayPopupPaddingBetweenItems));
-
- icon_ = new VolumeButton(this);
- AddChildView(icon_);
-
- slider_ = new VolumeSlider(this);
- AddChildView(slider_);
-
- bar_ = new BarSeparator;
- AddChildView(bar_);
-
- device_type_ = new views::ImageView;
- AddChildView(device_type_);
-
- 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();
- }
-
- // Sets volume level on slider_, |percent| is ranged from [0.00] to [1.00].
- void SetVolumeLevel(float percent) {
- // 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 1%.
- if (std::abs(percent-slider_->value()) < 0.01)
- return;
- // 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 (!ash::switches::ShowAudioDeviceMenu() || !is_default_view_) {
- more_->SetVisible(false);
- bar_->SetVisible(false);
- device_type_->SetVisible(false);
- return;
- }
-
- CrasAudioHandler* audio_handler = 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;
- if (!audio_handler->GetActiveOutputDevice(&device))
- return;
- int device_icon = GetAudioDeviceIconId(device.type);
- bar_->SetVisible(show_more);
- if (device_icon != kNoAudioDeviceIcon) {
- device_type_->SetVisible(true);
- device_type_->SetImage(
- ui::ResourceBundle::GetSharedInstance().GetImageNamed(
- device_icon).ToImageSkia());
- } else {
- device_type_->SetVisible(false);
- }
- }
-
- void HandleVolumeUp(int volume) {
- CrasAudioHandler* audio_handler = CrasAudioHandler::Get();
- audio_handler->SetOutputVolumePercent(volume);
- if (audio_handler->IsOutputMuted() &&
- !audio_handler->IsOutputVolumeBelowDefaultMuteLvel())
- audio_handler->SetOutputMute(false);
- }
-
- void HandleVolumeDown(int volume) {
- CrasAudioHandler* audio_handler = CrasAudioHandler::Get();
- audio_handler->SetOutputVolumePercent(volume);
- if (audio_handler->IsOutputVolumeBelowDefaultMuteLvel() &&
- !audio_handler->IsOutputMuted()) {
- audio_handler->SetOutputMute(true);
- } else if (!audio_handler->IsOutputVolumeBelowDefaultMuteLvel() &&
- audio_handler->IsOutputMuted()) {
- audio_handler->SetOutputMute(false);
- }
- }
-
- // 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 either bar_ or device_type_ at the left of the more_ button.
- views::View* view_left_to_more;
- if (device_type_->visible())
- view_left_to_more = device_type_;
- else
- view_left_to_more = bar_;
- gfx::Size view_size = view_left_to_more->GetPreferredSize();
- gfx::Rect view_bounds(view_size);
- view_bounds.set_x(more_->bounds().x() - view_size.width() -
- kExtraPaddingBetweenBarAndMore);
- view_bounds.set_y((height() - view_size.height()) / 2);
- view_left_to_more->SetBoundsRect(view_bounds);
-
- // Layout vertical bar next to view_left_to_more if device_type_ is visible.
- if (device_type_->visible()) {
- gfx::Size bar_size = bar_->GetPreferredSize();
- gfx::Rect bar_bounds(bar_size);
- bar_bounds.set_x(view_left_to_more->bounds().x() - bar_size.width());
- bar_bounds.set_y((height() - bar_size.height()) / 2);
- bar_->SetBoundsRect(bar_bounds);
- }
-
- // Layout slider, calculate slider width.
- gfx::Rect slider_bounds = slider_->bounds();
- slider_bounds.set_width(
- bar_->bounds().x()
- - (device_type_->visible() ? 0 : 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_);
- bool mute_on = !IsAudioMuted();
- CrasAudioHandler::Get()->SetOutputMute(mute_on);
- if (!mute_on)
- CrasAudioHandler::Get()->AdjustOutputVolumeToAudibleLevel();
- }
-
- // 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) {
- int volume = value * 100.0f;
- int old_volume = CrasAudioHandler::Get()->GetOutputVolumePercent();
- // Do not call change audio volume if the difference is less than
- // 1%, which is beyond cras audio api's granularity for output volume.
- if (std::abs(volume - old_volume) < 1)
- return;
- Shell::GetInstance()->metrics()->RecordUserMetricsAction(
- is_default_view_ ?
- ash::UMA_STATUS_AREA_CHANGED_VOLUME_MENU :
- ash::UMA_STATUS_AREA_CHANGED_VOLUME_POPUP);
- if (volume > old_volume)
- HandleVolumeUp(volume);
- else
- HandleVolumeDown(volume);
- }
- icon_->Update();
- }
-
- // Overriden from ActionableView.
- 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;
- 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.
- AddScrollListInfoItem(
- l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_OUTPUT));
- for (size_t i = 0; i < output_devices_.size(); ++i) {
- HoverHighlightView* container = AddScrollListItem(
- GetAudioDeviceName(output_devices_[i]),
- gfx::Font::NORMAL,
- output_devices_[i].active); /* checkmark if active */
- device_map_[container] = output_devices_[i];
- }
-
- AddScrollSeparator();
-
- // Add audio input devices.
- AddScrollListInfoItem(
- l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_INPUT));
- for (size_t i = 0; i < input_devices_.size(); ++i) {
- HoverHighlightView* container = AddScrollListItem(
- GetAudioDeviceName(input_devices_[i]),
- gfx::Font::NORMAL,
- input_devices_[i].active); /* checkmark if active */
- device_map_[container] = input_devices_[i];
- }
-
- scroll_content()->SizeToPreferredSize();
- scroller()->Layout();
- }
-
- void AddScrollListInfoItem(const base::string16& text) {
- views::Label* label = new views::Label(
- text,
- ui::ResourceBundle::GetSharedInstance().GetFontList(
- ui::ResourceBundle::BoldFont));
-
- // Align info item with checkbox items
- int margin = kTrayPopupPaddingHorizontal +
- kTrayPopupDetailsLabelExtraLeftMargin;
- int left_margin = 0;
- int right_margin = 0;
- if (base::i18n::IsRTL())
- right_margin = margin;
- else
- left_margin = margin;
-
- label->SetBorder(
- views::Border::CreateEmptyBorder(ash::kTrayPopupPaddingBetweenItems,
- left_margin,
- ash::kTrayPopupPaddingBetweenItems,
- right_margin));
- label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
- label->SetEnabledColor(SkColorSetARGB(192, 0, 0, 0));
-
- scroll_content()->AddChildView(label);
- }
-
- HoverHighlightView* AddScrollListItem(const base::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()) {
- TransitionToDefaultView();
- } else {
- AudioDeviceMap::iterator iter = device_map_.find(sender);
- if (iter == device_map_.end())
- return;
- chromeos::AudioDevice& device = iter->second;
- CrasAudioHandler::Get()->SwitchToDevice(device);
- }
- }
-
- 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) {
- CrasAudioHandler::Get()->AddAudioObserver(this);
-}
-
-TrayAudio::~TrayAudio() {
- if (CrasAudioHandler::IsInitialized())
- CrasAudioHandler::Get()->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 (!ash::switches::ShowAudioDeviceMenu() || pop_up_volume_view_) {
- volume_view_ = new tray::VolumeView(this, false);
- return volume_view_;
- } else {
- Shell::GetInstance()->metrics()->RecordUserMetricsAction(
- ash::UMA_STATUS_AREA_DETAILED_AUDIO_VIEW);
- 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::ShouldShowShelf() const {
- return ash::switches::ShowAudioDeviceMenu() && !pop_up_volume_view_;
-}
-
-void TrayAudio::OnOutputVolumeChanged() {
- 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() {
- if (tray_view())
- tray_view()->SetVisible(GetInitialVisibility());
-
- if (volume_view_) {
- volume_view_->Update();
- SetDetailedViewCloseDelay(kTrayPopupAutoCloseDelayInSeconds);
- } else {
- pop_up_volume_view_ = true;
- PopupDetailedView(kTrayPopupAutoCloseDelayInSeconds, false);
- }
-}
-
-void TrayAudio::OnInputGainChanged() {
-}
-
-void TrayAudio::OnInputMuteChanged() {
-}
-
-void TrayAudio::OnAudioNodesChanged() {
- Update();
-}
-
-void TrayAudio::OnActiveOutputNodeChanged() {
- Update();
-}
-
-void TrayAudio::OnActiveInputNodeChanged() {
- Update();
-}
-
-void TrayAudio::Update() {
- if (tray_view())
- tray_view()->SetVisible(GetInitialVisibility());
- if (audio_detail_)
- audio_detail_->Update();
- if (volume_view_) {
- volume_view_->SetVolumeLevel(GetVolumeLevel());
- volume_view_->Update();
- }
-}
-
-} // namespace internal
-} // namespace ash
diff --git a/ash/system/chromeos/audio/tray_audio_chromeos.cc b/ash/system/chromeos/audio/tray_audio_chromeos.cc
new file mode 100644
index 0000000..f80d712
--- /dev/null
+++ b/ash/system/chromeos/audio/tray_audio_chromeos.cc
@@ -0,0 +1,61 @@
+// Copyright 2014 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_chromeos.h"
+
+#include "ash/ash_switches.h"
+#include "ash/metrics/user_metrics_recorder.h"
+#include "ash/shell.h"
+#include "ash/system/audio/volume_view.h"
+#include "ash/system/chromeos/audio/audio_detailed_view.h"
+#include "ash/system/chromeos/audio/tray_audio_delegate_chromeos.h"
+#include "ui/views/view.h"
+
+namespace ash {
+
+using system::TrayAudioDelegate;
+using system::TrayAudioDelegateChromeOs;
+
+namespace internal {
+
+TrayAudioChromeOs::TrayAudioChromeOs(SystemTray* system_tray)
+ : TrayAudio(system_tray,
+ scoped_ptr<TrayAudioDelegate>(new TrayAudioDelegateChromeOs())),
+ audio_detail_view_(NULL) {
+}
+
+TrayAudioChromeOs::~TrayAudioChromeOs() {
+}
+
+void TrayAudioChromeOs::Update() {
+ TrayAudio::Update();
+
+ if (audio_detail_view_)
+ audio_detail_view_->Update();
+}
+
+views::View* TrayAudioChromeOs::CreateDetailedView(user::LoginStatus status) {
+ if (!ash::switches::ShowAudioDeviceMenu() || pop_up_volume_view_) {
+ volume_view_ = new tray::VolumeView(this, audio_delegate_.get(), false);
+ return volume_view_;
+ } else {
+ Shell::GetInstance()->metrics()->RecordUserMetricsAction(
+ ash::UMA_STATUS_AREA_DETAILED_AUDIO_VIEW);
+ audio_detail_view_ =
+ new tray::AudioDetailedView(this, status);
+ return audio_detail_view_;
+ }
+}
+
+void TrayAudioChromeOs::DestroyDetailedView() {
+ if (audio_detail_view_) {
+ audio_detail_view_ = NULL;
+ } else if (volume_view_) {
+ volume_view_ = NULL;
+ pop_up_volume_view_ = false;
+ }
+}
+
+} // namespace internal
+} // namespace ash
diff --git a/ash/system/chromeos/audio/tray_audio_chromeos.h b/ash/system/chromeos/audio/tray_audio_chromeos.h
new file mode 100644
index 0000000..6817f85
--- /dev/null
+++ b/ash/system/chromeos/audio/tray_audio_chromeos.h
@@ -0,0 +1,42 @@
+// Copyright 2014 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_CHROMEOS_AUDIO_TRAY_AUDIO_CHROMEOS_H_
+#define ASH_SYSTEM_CHROMEOS_AUDIO_TRAY_AUDIO_CHROMEOS_H_
+
+#include "ash/ash_export.h"
+#include "ash/system/audio/tray_audio.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace ash {
+
+namespace internal {
+
+namespace tray {
+class AudioDetailedView;
+}
+
+class ASH_EXPORT TrayAudioChromeOs : public TrayAudio {
+ public:
+ explicit TrayAudioChromeOs(SystemTray* system_tray);
+ virtual ~TrayAudioChromeOs();
+
+ protected:
+ // Overridden from TrayAudio
+ virtual void Update() OVERRIDE;
+
+ private:
+ // Overridden from SystemTrayItem.
+ virtual views::View* CreateDetailedView(user::LoginStatus status) OVERRIDE;
+ virtual void DestroyDetailedView() OVERRIDE;
+
+ tray::AudioDetailedView* audio_detail_view_;
+
+ DISALLOW_COPY_AND_ASSIGN(TrayAudioChromeOs);
+};
+
+} // namespace internal
+} // namespace ash
+
+#endif // ASH_SYSTEM_CHROMEOS_AUDIO_TRAY_AUDIO_CHROMEOS_H_
diff --git a/ash/system/chromeos/audio/tray_audio_delegate_chromeos.cc b/ash/system/chromeos/audio/tray_audio_delegate_chromeos.cc
new file mode 100644
index 0000000..59f6ecf
--- /dev/null
+++ b/ash/system/chromeos/audio/tray_audio_delegate_chromeos.cc
@@ -0,0 +1,65 @@
+// Copyright 2014 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_delegate_chromeos.h"
+
+#include "chromeos/audio/cras_audio_handler.h"
+#include "grit/ash_resources.h"
+#include "grit/ash_strings.h"
+
+using chromeos::CrasAudioHandler;
+
+namespace ash {
+namespace system {
+
+void TrayAudioDelegateChromeOs::AdjustOutputVolumeToAudibleLevel() {
+ CrasAudioHandler::Get()->AdjustOutputVolumeToAudibleLevel();
+}
+
+int TrayAudioDelegateChromeOs::GetOutputDefaultVolumeMuteLevel() {
+ return CrasAudioHandler::Get()->GetOutputDefaultVolumeMuteThreshold();
+}
+
+int TrayAudioDelegateChromeOs::GetOutputVolumeLevel() {
+ return CrasAudioHandler::Get()->GetOutputVolumePercent();
+}
+
+int TrayAudioDelegateChromeOs::GetActiveOutputDeviceIconId() {
+ chromeos::AudioDevice device;
+ if (!CrasAudioHandler::Get()->GetActiveOutputDevice(&device))
+ return kNoAudioDeviceIcon;
+
+ if (device.type == chromeos::AUDIO_TYPE_HEADPHONE)
+ return IDR_AURA_UBER_TRAY_AUDIO_HEADPHONE;
+ else if (device.type == chromeos::AUDIO_TYPE_USB)
+ return IDR_AURA_UBER_TRAY_AUDIO_USB;
+ else if (device.type == chromeos::AUDIO_TYPE_BLUETOOTH)
+ return IDR_AURA_UBER_TRAY_AUDIO_BLUETOOTH;
+ else if (device.type == chromeos::AUDIO_TYPE_HDMI)
+ return IDR_AURA_UBER_TRAY_AUDIO_HDMI;
+ else
+ return kNoAudioDeviceIcon;
+}
+
+
+bool TrayAudioDelegateChromeOs::HasAlternativeSources() {
+ CrasAudioHandler* audio_handler = CrasAudioHandler::Get();
+ return (audio_handler->has_alternative_output() ||
+ audio_handler->has_alternative_input());
+}
+
+bool TrayAudioDelegateChromeOs::IsOutputAudioMuted() {
+ return CrasAudioHandler::Get()->IsOutputMuted();
+}
+
+void TrayAudioDelegateChromeOs::SetOutputAudioIsMuted(bool is_muted) {
+ CrasAudioHandler::Get()->SetOutputMute(is_muted);
+}
+
+void TrayAudioDelegateChromeOs::SetOutputVolumeLevel(int level) {
+ CrasAudioHandler::Get()->SetOutputVolumePercent(level);
+}
+
+} // namespace system
+} // namespace ash
diff --git a/ash/system/chromeos/audio/tray_audio_delegate_chromeos.h b/ash/system/chromeos/audio/tray_audio_delegate_chromeos.h
new file mode 100644
index 0000000..58e714d
--- /dev/null
+++ b/ash/system/chromeos/audio/tray_audio_delegate_chromeos.h
@@ -0,0 +1,33 @@
+// Copyright 2014 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_TRAY_AUDIO_DELEGATE_CHROMEOS_H_
+#define ASH_SYSTEM_AUDIO_TRAY_AUDIO_DELEGATE_CHROMEOS_H_
+
+#include "ash/ash_export.h"
+#include "ash/system/audio/tray_audio_delegate.h"
+#include "base/compiler_specific.h"
+
+namespace ash {
+namespace system {
+
+class ASH_EXPORT TrayAudioDelegateChromeOs : public TrayAudioDelegate {
+ public:
+ virtual ~TrayAudioDelegateChromeOs() {}
+
+ // Overridden from TrayAudioDelegate.
+ virtual void AdjustOutputVolumeToAudibleLevel() OVERRIDE;
+ virtual int GetOutputDefaultVolumeMuteLevel() OVERRIDE;
+ virtual int GetOutputVolumeLevel() OVERRIDE;
+ virtual int GetActiveOutputDeviceIconId() OVERRIDE;
+ virtual bool HasAlternativeSources() OVERRIDE;
+ virtual bool IsOutputAudioMuted() OVERRIDE;
+ virtual void SetOutputAudioIsMuted(bool is_muted) OVERRIDE;
+ virtual void SetOutputVolumeLevel(int level) OVERRIDE;
+};
+
+} // namespace system
+} // namespace ash
+
+#endif // ASH_SYSTEM_AUDIO_TRAY_AUDIO_DELEGATE_CHROMEOS_H_
diff --git a/ash/system/tray/system_tray.cc b/ash/system/tray/system_tray.cc
index 942b15c..7b34dee 100644
--- a/ash/system/tray/system_tray.cc
+++ b/ash/system/tray/system_tray.cc
@@ -10,6 +10,7 @@
#include "ash/shell.h"
#include "ash/shell/panel_window.h"
#include "ash/shell_window_ids.h"
+#include "ash/system/audio/tray_audio.h"
#include "ash/system/bluetooth/tray_bluetooth.h"
#include "ash/system/date/tray_date.h"
#include "ash/system/drive/tray_drive.h"
@@ -47,7 +48,7 @@
#include "ui/views/view.h"
#if defined(OS_CHROMEOS)
-#include "ash/system/chromeos/audio/tray_audio.h"
+#include "ash/system/chromeos/audio/tray_audio_chromeos.h"
#include "ash/system/chromeos/brightness/tray_brightness.h"
#include "ash/system/chromeos/enterprise/tray_enterprise.h"
#include "ash/system/chromeos/managed/tray_locally_managed_user.h"
@@ -195,7 +196,7 @@ void SystemTray::CreateItems(SystemTrayDelegate* delegate) {
AddTrayItem(new internal::TrayDisplay(this));
AddTrayItem(new internal::ScreenCaptureTrayItem(this));
AddTrayItem(new internal::ScreenShareTrayItem(this));
- AddTrayItem(new internal::TrayAudio(this));
+ AddTrayItem(new internal::TrayAudioChromeOs(this));
AddTrayItem(new internal::TrayBrightness(this));
AddTrayItem(new internal::TrayCapsLock(this));
AddTrayItem(new internal::TraySettings(this));
diff --git a/ash/system/tray/system_tray_notifier.cc b/ash/system/tray/system_tray_notifier.cc
index 95b6789..6a2cc79 100644
--- a/ash/system/tray/system_tray_notifier.cc
+++ b/ash/system/tray/system_tray_notifier.cc
@@ -29,6 +29,14 @@ void SystemTrayNotifier::RemoveAccessibilityObserver(
accessibility_observers_.RemoveObserver(observer);
}
+void SystemTrayNotifier::AddAudioObserver(AudioObserver* observer) {
+ audio_observers_.AddObserver(observer);
+}
+
+void SystemTrayNotifier::RemoveAudioObserver(AudioObserver* observer) {
+ audio_observers_.RemoveObserver(observer);
+}
+
void SystemTrayNotifier::AddBluetoothObserver(BluetoothObserver* observer) {
bluetooth_observers_.AddObserver(observer);
}
@@ -170,6 +178,41 @@ void SystemTrayNotifier::NotifyAccessibilityModeChanged(
OnAccessibilityModeChanged(notify));
}
+void SystemTrayNotifier::NotifyAudioOutputVolumeChanged() {
+ FOR_EACH_OBSERVER(
+ AudioObserver,
+ audio_observers_,
+ OnOutputVolumeChanged());
+}
+
+void SystemTrayNotifier::NotifyAudioOutputMuteChanged() {
+ FOR_EACH_OBSERVER(
+ AudioObserver,
+ audio_observers_,
+ OnOutputMuteChanged());
+}
+
+void SystemTrayNotifier::NotifyAudioNodesChanged() {
+ FOR_EACH_OBSERVER(
+ AudioObserver,
+ audio_observers_,
+ OnAudioNodesChanged());
+}
+
+void SystemTrayNotifier::NotifyAudioActiveOutputNodeChanged() {
+ FOR_EACH_OBSERVER(
+ AudioObserver,
+ audio_observers_,
+ OnActiveOutputNodeChanged());
+}
+
+void SystemTrayNotifier::NotifyAudioActiveInputNodeChanged() {
+ FOR_EACH_OBSERVER(
+ AudioObserver,
+ audio_observers_,
+ OnActiveInputNodeChanged());
+}
+
void SystemTrayNotifier::NotifyTracingModeChanged(bool value) {
FOR_EACH_OBSERVER(
TracingObserver,
diff --git a/ash/system/tray/system_tray_notifier.h b/ash/system/tray/system_tray_notifier.h
index 6124040..bfd7ff0 100644
--- a/ash/system/tray/system_tray_notifier.h
+++ b/ash/system/tray/system_tray_notifier.h
@@ -9,6 +9,7 @@
#include <vector>
#include "ash/ash_export.h"
+#include "ash/system/audio/audio_observer.h"
#include "ash/system/bluetooth/bluetooth_observer.h"
#include "ash/system/chromeos/tray_tracing.h"
#include "ash/system/date/clock_observer.h"
@@ -38,13 +39,16 @@ class NetworkStateNotifier;
#endif
class ASH_EXPORT SystemTrayNotifier {
-public:
+ public:
SystemTrayNotifier();
~SystemTrayNotifier();
void AddAccessibilityObserver(AccessibilityObserver* observer);
void RemoveAccessibilityObserver(AccessibilityObserver* observer);
+ void AddAudioObserver(AudioObserver* observer);
+ void RemoveAudioObserver(AudioObserver* observer);
+
void AddBluetoothObserver(BluetoothObserver* observer);
void RemoveBluetoothObserver(BluetoothObserver* observer);
@@ -94,6 +98,11 @@ public:
void NotifyAccessibilityModeChanged(
AccessibilityNotificationVisibility notify);
+ void NotifyAudioOutputVolumeChanged();
+ void NotifyAudioOutputMuteChanged();
+ void NotifyAudioNodesChanged();
+ void NotifyAudioActiveOutputNodeChanged();
+ void NotifyAudioActiveInputNodeChanged();
void NotifyTracingModeChanged(bool value);
void NotifyRefreshBluetooth();
void NotifyBluetoothDiscoveringChanged();
@@ -131,6 +140,7 @@ public:
private:
ObserverList<AccessibilityObserver> accessibility_observers_;
+ ObserverList<AudioObserver> audio_observers_;
ObserverList<BluetoothObserver> bluetooth_observers_;
ObserverList<CapsLockObserver> caps_lock_observers_;
ObserverList<ClockObserver> clock_observers_;
diff --git a/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc b/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
index 5b6ec0a..2c320e9 100644
--- a/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
+++ b/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
@@ -314,6 +314,9 @@ void SystemTrayDelegateChromeOS::Initialize() {
if (LoginState::IsInitialized())
LoginState::Get()->AddObserver(this);
+
+ if (CrasAudioHandler::IsInitialized())
+ CrasAudioHandler::Get()->AddAudioObserver(this);
}
void SystemTrayDelegateChromeOS::Shutdown() {
@@ -369,7 +372,10 @@ SystemTrayDelegateChromeOS::~SystemTrayDelegateChromeOS() {
->RemoveSessionStateObserver(this);
LoginState::Get()->RemoveObserver(this);
- // Stop observing Drive operations.
+ if (CrasAudioHandler::IsInitialized())
+ CrasAudioHandler::Get()->RemoveAudioObserver(this);
+
+// Stop observing Drive operations.
UnobserveDriveUpdates();
policy::BrowserPolicyConnectorChromeOS* connector =
@@ -1210,6 +1216,33 @@ void SystemTrayDelegateChromeOS::InputMethodMenuItemChanged(
GetSystemTrayNotifier()->NotifyRefreshIME(false);
}
+// Overridden from CrasAudioHandler::AudioObserver.
+void SystemTrayDelegateChromeOS::OnOutputVolumeChanged() {
+ GetSystemTrayNotifier()->NotifyAudioOutputVolumeChanged();
+}
+
+void SystemTrayDelegateChromeOS::OnOutputMuteChanged() {
+ GetSystemTrayNotifier()->NotifyAudioOutputMuteChanged();
+}
+
+void SystemTrayDelegateChromeOS::OnInputGainChanged() {
+}
+
+void SystemTrayDelegateChromeOS::OnInputMuteChanged() {
+}
+
+void SystemTrayDelegateChromeOS::OnAudioNodesChanged() {
+ GetSystemTrayNotifier()->NotifyAudioNodesChanged();
+}
+
+void SystemTrayDelegateChromeOS::OnActiveOutputNodeChanged() {
+ GetSystemTrayNotifier()->NotifyAudioActiveOutputNodeChanged();
+}
+
+void SystemTrayDelegateChromeOS::OnActiveInputNodeChanged() {
+ GetSystemTrayNotifier()->NotifyAudioActiveInputNodeChanged();
+}
+
// drive::JobListObserver overrides.
void SystemTrayDelegateChromeOS::OnJobAdded(const drive::JobInfo& job_info) {
OnJobUpdated(job_info);
diff --git a/chrome/browser/ui/ash/system_tray_delegate_chromeos.h b/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
index f465dd3..ec6fb22 100644
--- a/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
+++ b/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
@@ -20,6 +20,7 @@
#include "chrome/browser/chromeos/events/system_key_event_listener.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/ash/system_tray_delegate_chromeos.h"
+#include "chromeos/audio/cras_audio_handler.h"
#include "chromeos/dbus/session_manager_client.h"
#include "chromeos/ime/input_method_manager.h"
#include "chromeos/login/login_state.h"
@@ -38,6 +39,7 @@ class SystemTrayDelegateChromeOS
public content::NotificationObserver,
public input_method::InputMethodManager::Observer,
public chromeos::LoginState::Observer,
+ public chromeos::CrasAudioHandler::AudioObserver,
public device::BluetoothAdapter::Observer,
public SystemKeyEventListener::CapsLockObserver,
public policy::CloudPolicyStore::Observer,
@@ -186,6 +188,15 @@ class SystemTrayDelegateChromeOS
virtual void InputMethodMenuItemChanged(
ash::ime::InputMethodMenuManager* manager) OVERRIDE;
+ // Overridden from CrasAudioHandler::AudioObserver.
+ virtual void OnOutputVolumeChanged() OVERRIDE;
+ virtual void OnOutputMuteChanged() OVERRIDE;
+ virtual void OnInputGainChanged() OVERRIDE;
+ virtual void OnInputMuteChanged() OVERRIDE;
+ virtual void OnAudioNodesChanged() OVERRIDE;
+ virtual void OnActiveOutputNodeChanged() OVERRIDE;
+ virtual void OnActiveInputNodeChanged() OVERRIDE;
+
// drive::JobListObserver overrides.
virtual void OnJobAdded(const drive::JobInfo& job_info) OVERRIDE;
diff --git a/chromeos/audio/cras_audio_handler.cc b/chromeos/audio/cras_audio_handler.cc
index 0045f84..fd2d92e 100644
--- a/chromeos/audio/cras_audio_handler.cc
+++ b/chromeos/audio/cras_audio_handler.cc
@@ -130,6 +130,10 @@ bool CrasAudioHandler::IsInputMutedForDevice(uint64 device_id) {
return audio_pref_handler_->GetMuteValue(*device);
}
+int CrasAudioHandler::GetOutputDefaultVolumeMuteThreshold() {
+ return kMuteThresholdPercent;
+}
+
int CrasAudioHandler::GetOutputVolumePercent() {
return output_volume_;
}
diff --git a/chromeos/audio/cras_audio_handler.h b/chromeos/audio/cras_audio_handler.h
index f2a35a5..cc28c50 100644
--- a/chromeos/audio/cras_audio_handler.h
+++ b/chromeos/audio/cras_audio_handler.h
@@ -99,6 +99,9 @@ class CHROMEOS_EXPORT CrasAudioHandler : public CrasAudioClient::Observer,
// Returns true if the output volume is below the default mute volume level.
virtual bool IsOutputVolumeBelowDefaultMuteLvel();
+ // Returns volume level in 0-100% range at which the volume should be muted.
+ virtual int GetOutputDefaultVolumeMuteThreshold();
+
// Gets volume level in 0-100% range (0 being pure silence) for the current
// active node.
virtual int GetOutputVolumePercent();