diff options
author | zturner@chromium.org <zturner@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-02-19 20:25:56 +0000 |
---|---|---|
committer | zturner@chromium.org <zturner@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-02-19 20:25:56 +0000 |
commit | 667a0084e28414688620c50b8c342ba3bdeb8456 (patch) | |
tree | b35725e99614c70b0f9e26a80a3d81bed2b45351 | |
parent | e25b34a0c9dc137eb2fce6802ac3c4962c159060 (diff) | |
download | chromium_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
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(); |