blob: 6ad4362a4419cd831900197a1c78869fb8ab67fb [file] [log] [blame]
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ash/system/chromeos/audio/tray_audio.h"
6
7#include <cmath>
8
9#include "ash/ash_constants.h"
10#include "ash/ash_switches.h"
11#include "ash/shell.h"
12#include "ash/system/tray/actionable_view.h"
13#include "ash/system/tray/fixed_sized_scroll_view.h"
14#include "ash/system/tray/hover_highlight_view.h"
15#include "ash/system/tray/system_tray.h"
16#include "ash/system/tray/system_tray_delegate.h"
17#include "ash/system/tray/system_tray_notifier.h"
18#include "ash/system/tray/tray_constants.h"
19#include "ash/volume_control_delegate.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010020#include "base/strings/utf_string_conversions.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010021#include "chromeos/audio/cras_audio_handler.h"
22#include "grit/ash_resources.h"
23#include "grit/ash_strings.h"
24#include "third_party/skia/include/core/SkCanvas.h"
25#include "third_party/skia/include/core/SkPaint.h"
26#include "third_party/skia/include/core/SkRect.h"
27#include "third_party/skia/include/effects/SkGradientShader.h"
28#include "ui/base/l10n/l10n_util.h"
29#include "ui/base/resource/resource_bundle.h"
30#include "ui/gfx/canvas.h"
31#include "ui/gfx/image/image.h"
32#include "ui/gfx/image/image_skia_operations.h"
33#include "ui/views/controls/button/image_button.h"
34#include "ui/views/controls/image_view.h"
35#include "ui/views/controls/label.h"
36#include "ui/views/controls/slider.h"
37#include "ui/views/layout/box_layout.h"
38#include "ui/views/view.h"
39
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010040using chromeos::CrasAudioHandler;
41
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010042namespace ash {
43namespace internal {
44
45namespace {
46const int kVolumeImageWidth = 25;
47const int kVolumeImageHeight = 25;
48const int kBarSeparatorWidth = 25;
49const int kBarSeparatorHeight = 30;
50const int kSliderRightPaddingToVolumeViewEdge = 17;
51const int kExtraPaddingBetweenBarAndMore = 10;
52
53const int kNoAudioDeviceIcon = -1;
54
55// IDR_AURA_UBER_TRAY_VOLUME_LEVELS contains 5 images,
56// The one for mute is at the 0 index and the other
57// four are used for ascending volume levels.
58const int kVolumeLevels = 4;
59
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010060bool IsAudioMuted() {
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010061 return CrasAudioHandler::Get()->IsOutputMuted();
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010062}
63
64float GetVolumeLevel() {
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010065 return CrasAudioHandler::Get()->GetOutputVolumePercent() / 100.0f;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010066}
67
68int GetAudioDeviceIconId(chromeos::AudioDeviceType type) {
69 if (type == chromeos::AUDIO_TYPE_HEADPHONE)
70 return IDR_AURA_UBER_TRAY_AUDIO_HEADPHONE;
71 else if (type == chromeos::AUDIO_TYPE_USB)
72 return IDR_AURA_UBER_TRAY_AUDIO_USB;
73 else if (type == chromeos::AUDIO_TYPE_BLUETOOTH)
74 return IDR_AURA_UBER_TRAY_AUDIO_BLUETOOTH;
75 else if (type == chromeos::AUDIO_TYPE_HDMI)
76 return IDR_AURA_UBER_TRAY_AUDIO_HDMI;
77 else
78 return kNoAudioDeviceIcon;
79}
80
81} // namespace
82
83namespace tray {
84
85class VolumeButton : public views::ToggleImageButton {
86 public:
87 explicit VolumeButton(views::ButtonListener* listener)
88 : views::ToggleImageButton(listener),
89 image_index_(-1) {
90 SetImageAlignment(ALIGN_CENTER, ALIGN_MIDDLE);
91 image_ = ui::ResourceBundle::GetSharedInstance().GetImageNamed(
92 IDR_AURA_UBER_TRAY_VOLUME_LEVELS);
93 SetPreferredSize(gfx::Size(kTrayPopupItemHeight, kTrayPopupItemHeight));
94 Update();
95 }
96
97 virtual ~VolumeButton() {}
98
99 void Update() {
100 float level = GetVolumeLevel();
101 int image_index = IsAudioMuted() ?
102 0 : (level == 1.0 ?
103 kVolumeLevels :
104 std::max(1, int(std::ceil(level * (kVolumeLevels - 1)))));
105 if (image_index != image_index_) {
106 gfx::Rect region(0, image_index * kVolumeImageHeight,
107 kVolumeImageWidth, kVolumeImageHeight);
108 gfx::ImageSkia image_skia = gfx::ImageSkiaOperations::ExtractSubset(
109 *(image_.ToImageSkia()), region);
110 SetImage(views::CustomButton::STATE_NORMAL, &image_skia);
111 image_index_ = image_index;
112 }
113 SchedulePaint();
114 }
115
116 private:
117 // Overridden from views::View.
118 virtual gfx::Size GetPreferredSize() OVERRIDE {
119 gfx::Size size = views::ToggleImageButton::GetPreferredSize();
120 size.set_height(kTrayPopupItemHeight);
121 return size;
122 }
123
124 gfx::Image image_;
125 int image_index_;
126
127 DISALLOW_COPY_AND_ASSIGN(VolumeButton);
128};
129
130class VolumeSlider : public views::Slider {
131 public:
132 explicit VolumeSlider(views::SliderListener* listener)
133 : views::Slider(listener, views::Slider::HORIZONTAL) {
134 set_focus_border_color(kFocusBorderColor);
135 SetValue(GetVolumeLevel());
136 SetAccessibleName(
137 ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
138 IDS_ASH_STATUS_TRAY_VOLUME));
139 Update();
140 }
141 virtual ~VolumeSlider() {}
142
143 void Update() {
144 UpdateState(!IsAudioMuted());
145 }
146
147 DISALLOW_COPY_AND_ASSIGN(VolumeSlider);
148};
149
150// Vertical bar separator that can be placed on the VolumeView.
151class BarSeparator : public views::View {
152 public:
153 BarSeparator() {}
154 virtual ~BarSeparator() {}
155
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100156 // Overriden from views::View.
157 virtual gfx::Size GetPreferredSize() OVERRIDE {
158 return gfx::Size(kBarSeparatorWidth, kBarSeparatorHeight);
159 }
160
Ben Murdochbb1529c2013-08-08 10:24:53 +0100161 private:
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100162 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
163 canvas->FillRect(gfx::Rect(width() / 2, 0, 1, height()),
164 kButtonStrokeColor);
165 }
166
167 DISALLOW_COPY_AND_ASSIGN(BarSeparator);
168};
169
170class VolumeView : public ActionableView,
171 public views::ButtonListener,
172 public views::SliderListener {
173 public:
174 VolumeView(SystemTrayItem* owner, bool is_default_view)
175 : owner_(owner),
176 icon_(NULL),
177 slider_(NULL),
178 bar_(NULL),
179 device_type_(NULL),
180 more_(NULL),
181 is_default_view_(is_default_view) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100182 set_focusable(false);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100183 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal,
184 kTrayPopupPaddingHorizontal, 0, kTrayPopupPaddingBetweenItems));
185
186 icon_ = new VolumeButton(this);
187 AddChildView(icon_);
188
189 slider_ = new VolumeSlider(this);
190 AddChildView(slider_);
191
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100192 bar_ = new BarSeparator;
193 AddChildView(bar_);
194
Ben Murdochbb1529c2013-08-08 10:24:53 +0100195 device_type_ = new views::ImageView;
196 AddChildView(device_type_);
197
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100198 more_ = new views::ImageView;
199 more_->EnableCanvasFlippingForRTLUI(true);
200 more_->SetImage(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
201 IDR_AURA_UBER_TRAY_MORE).ToImageSkia());
202 AddChildView(more_);
203
204 Update();
205 }
206
207 virtual ~VolumeView() {}
208
209 void Update() {
210 icon_->Update();
211 slider_->Update();
212 UpdateDeviceTypeAndMore();
213 Layout();
214 }
215
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100216 // Sets volume level on slider_, |percent| is ranged from [0.00] to [1.00].
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100217 void SetVolumeLevel(float percent) {
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100218 // Slider's value is in finer granularity than audio volume level(0.01),
219 // there will be a small discrepancy between slider's value and volume level
220 // on audio side. To avoid the jittering in slider UI, do not set change
221 // slider value if the change is less than 1%.
222 if (std::abs(percent-slider_->value()) < 0.01)
223 return;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100224 // The change in volume will be reflected via accessibility system events,
225 // so we prevent the UI event from being sent here.
226 slider_->set_enable_accessibility_events(false);
227 slider_->SetValue(percent);
228 // It is possible that the volume was (un)muted, but the actual volume level
229 // did not change. In that case, setting the value of the slider won't
230 // trigger an update. So explicitly trigger an update.
231 Update();
232 slider_->set_enable_accessibility_events(true);
233 }
234
235 private:
236 // Updates bar_, device_type_ icon, and more_ buttons.
237 void UpdateDeviceTypeAndMore() {
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100238 if (!ash::switches::ShowAudioDeviceMenu() || !is_default_view_) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100239 more_->SetVisible(false);
240 bar_->SetVisible(false);
241 device_type_->SetVisible(false);
242 return;
243 }
244
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100245 CrasAudioHandler* audio_handler = CrasAudioHandler::Get();
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100246 bool show_more = audio_handler->has_alternative_output() ||
247 audio_handler->has_alternative_input();
248 more_->SetVisible(show_more);
249
250 // Show output device icon if necessary.
251 chromeos::AudioDevice device;
Ben Murdochca12bfa2013-07-23 11:17:05 +0100252 if (!audio_handler->GetActiveOutputDevice(&device))
253 return;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100254 int device_icon = GetAudioDeviceIconId(device.type);
Ben Murdochbb1529c2013-08-08 10:24:53 +0100255 bar_->SetVisible(show_more);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100256 if (device_icon != kNoAudioDeviceIcon) {
257 device_type_->SetVisible(true);
258 device_type_->SetImage(
259 ui::ResourceBundle::GetSharedInstance().GetImageNamed(
260 device_icon).ToImageSkia());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100261 } else {
262 device_type_->SetVisible(false);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100263 }
264 }
265
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100266 void HandleVolumeUp(int volume) {
267 CrasAudioHandler* audio_handler = CrasAudioHandler::Get();
268 audio_handler->SetOutputVolumePercent(volume);
269 if (audio_handler->IsOutputMuted() &&
270 !audio_handler->IsOutputVolumeBelowDefaultMuteLvel())
271 audio_handler->SetOutputMute(false);
272 }
273
274 void HandleVolumeDown(int volume) {
275 CrasAudioHandler* audio_handler = CrasAudioHandler::Get();
276 audio_handler->SetOutputVolumePercent(volume);
277 if (audio_handler->IsOutputVolumeBelowDefaultMuteLvel() &&
278 !audio_handler->IsOutputMuted()) {
279 audio_handler->SetOutputMute(true);
280 } else if (!audio_handler->IsOutputVolumeBelowDefaultMuteLvel() &&
281 audio_handler->IsOutputMuted()) {
282 audio_handler->SetOutputMute(false);
283 }
284 }
285
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100286 // Overridden from views::View.
287 virtual void Layout() OVERRIDE {
288 views::View::Layout();
289
290 if (!more_->visible()) {
291 int w = width() - slider_->bounds().x() -
292 kSliderRightPaddingToVolumeViewEdge;
293 slider_->SetSize(gfx::Size(w, slider_->height()));
294 return;
295 }
296
297 // Make sure the chevron always has the full size.
298 gfx::Size size = more_->GetPreferredSize();
299 gfx::Rect bounds(size);
300 bounds.set_x(width() - size.width() - kTrayPopupPaddingBetweenItems);
301 bounds.set_y((height() - size.height()) / 2);
302 more_->SetBoundsRect(bounds);
303
Ben Murdochbb1529c2013-08-08 10:24:53 +0100304 // Layout either bar_ or device_type_ at the left of the more_ button.
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100305 views::View* view_left_to_more;
Ben Murdochbb1529c2013-08-08 10:24:53 +0100306 if (device_type_->visible())
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100307 view_left_to_more = device_type_;
Ben Murdochbb1529c2013-08-08 10:24:53 +0100308 else
309 view_left_to_more = bar_;
310 gfx::Size view_size = view_left_to_more->GetPreferredSize();
311 gfx::Rect view_bounds(view_size);
312 view_bounds.set_x(more_->bounds().x() - view_size.width() -
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100313 kExtraPaddingBetweenBarAndMore);
Ben Murdochbb1529c2013-08-08 10:24:53 +0100314 view_bounds.set_y((height() - view_size.height()) / 2);
315 view_left_to_more->SetBoundsRect(view_bounds);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100316
Ben Murdochbb1529c2013-08-08 10:24:53 +0100317 // Layout vertical bar next to view_left_to_more if device_type_ is visible.
318 if (device_type_->visible()) {
319 gfx::Size bar_size = bar_->GetPreferredSize();
320 gfx::Rect bar_bounds(bar_size);
321 bar_bounds.set_x(view_left_to_more->bounds().x() - bar_size.width());
322 bar_bounds.set_y((height() - bar_size.height()) / 2);
323 bar_->SetBoundsRect(bar_bounds);
324 }
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100325
326 // Layout slider, calculate slider width.
327 gfx::Rect slider_bounds = slider_->bounds();
328 slider_bounds.set_width(
Ben Murdochbb1529c2013-08-08 10:24:53 +0100329 bar_->bounds().x()
330 - (device_type_->visible() ? 0 : kTrayPopupPaddingBetweenItems)
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100331 - slider_bounds.x());
332 slider_->SetBoundsRect(slider_bounds);
333 }
334
335 // Overridden from views::ButtonListener.
336 virtual void ButtonPressed(views::Button* sender,
337 const ui::Event& event) OVERRIDE {
338 CHECK(sender == icon_);
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100339 bool mute_on = !IsAudioMuted();
340 CrasAudioHandler::Get()->SetOutputMute(mute_on);
341 if (!mute_on)
342 CrasAudioHandler::Get()->AdjustOutputVolumeToAudibleLevel();
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100343 }
344
345 // Overridden from views:SliderListener.
346 virtual void SliderValueChanged(views::Slider* sender,
347 float value,
348 float old_value,
349 views::SliderChangeReason reason) OVERRIDE {
350 if (reason == views::VALUE_CHANGED_BY_USER) {
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100351 int volume = value * 100.0f;
352 int old_volume = CrasAudioHandler::Get()->GetOutputVolumePercent();
353 // Do not call change audio volume if the difference is less than
354 // 1%, which is beyond cras audio api's granularity for output volume.
355 if (std::abs(volume - old_volume) < 1)
356 return;
357 if (volume > old_volume)
358 HandleVolumeUp(volume);
359 else
360 HandleVolumeDown(volume);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100361 }
362 icon_->Update();
363 }
364
365 // Overriden from ActinableView.
366 virtual bool PerformAction(const ui::Event& event) OVERRIDE {
367 if (!more_->visible())
368 return false;
369 owner_->TransitionDetailedView();
370 return true;
371 }
372
373 SystemTrayItem* owner_;
374 VolumeButton* icon_;
375 VolumeSlider* slider_;
376 BarSeparator* bar_;
377 views::ImageView* device_type_;
378 views::ImageView* more_;
379 bool is_default_view_;
380
381 DISALLOW_COPY_AND_ASSIGN(VolumeView);
382};
383
384class AudioDetailedView : public TrayDetailsView,
385 public ViewClickListener {
386 public:
387 AudioDetailedView(SystemTrayItem* owner, user::LoginStatus login)
388 : TrayDetailsView(owner),
389 login_(login) {
390 CreateItems();
391 Update();
392 }
393
394 virtual ~AudioDetailedView() {
395 }
396
397 void Update() {
398 UpdateAudioDevices();
399 Layout();
400 }
401
402 private:
403 void CreateItems() {
404 CreateScrollableList();
405 CreateHeaderEntry();
406 }
407
408 void CreateHeaderEntry() {
409 CreateSpecialRow(IDS_ASH_STATUS_TRAY_AUDIO, this);
410 }
411
412 void UpdateAudioDevices() {
413 output_devices_.clear();
414 input_devices_.clear();
415 chromeos::AudioDeviceList devices;
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100416 CrasAudioHandler::Get()->GetAudioDevices(&devices);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100417 for (size_t i = 0; i < devices.size(); ++i) {
418 if (devices[i].is_input)
419 input_devices_.push_back(devices[i]);
420 else
421 output_devices_.push_back(devices[i]);
422 }
423 UpdateScrollableList();
424 }
425
426 void UpdateScrollableList() {
427 scroll_content()->RemoveAllChildViews(true);
428 device_map_.clear();
429
430 // Add audio output devices.
431 AddScrollListItem(
432 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_OUTPUT),
433 gfx::Font::BOLD,
434 false); /* no checkmark */
435 for (size_t i = 0; i < output_devices_.size(); ++i) {
436 HoverHighlightView* container = AddScrollListItem(
Ben Murdocheb525c52013-07-10 11:40:50 +0100437 UTF8ToUTF16(output_devices_[i].display_name),
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100438 gfx::Font::NORMAL,
439 output_devices_[i].active); /* checkmark if active */
440 device_map_[container] = output_devices_[i];
441 }
442
443 AddScrollSeparator();
444
445 // Add audio input devices.
446 AddScrollListItem(
447 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_INPUT),
448 gfx::Font::BOLD,
449 false); /* no checkmark */
450 for (size_t i = 0; i < input_devices_.size(); ++i) {
451 HoverHighlightView* container = AddScrollListItem(
Ben Murdocheb525c52013-07-10 11:40:50 +0100452 UTF8ToUTF16(input_devices_[i].display_name),
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100453 gfx::Font::NORMAL,
454 input_devices_[i].active); /* checkmark if active */
455 device_map_[container] = input_devices_[i];
456 }
457
458 scroll_content()->SizeToPreferredSize();
459 scroller()->Layout();
460 }
461
462 HoverHighlightView* AddScrollListItem(const string16& text,
463 gfx::Font::FontStyle style,
464 bool checked) {
465 HoverHighlightView* container = new HoverHighlightView(this);
466 container->AddCheckableLabel(text, style, checked);
467 scroll_content()->AddChildView(container);
468 return container;
469 }
470
471 // Overridden from ViewClickListener.
472 virtual void OnViewClicked(views::View* sender) OVERRIDE {
473 if (sender == footer()->content()) {
474 owner()->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
475 } else {
476 AudioDeviceMap::iterator iter = device_map_.find(sender);
477 if (iter == device_map_.end())
478 return;
479 chromeos::AudioDevice& device = iter->second;
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100480 CrasAudioHandler::Get()->SwitchToDevice(device);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100481 }
482 }
483
484 typedef std::map<views::View*, chromeos::AudioDevice> AudioDeviceMap;
485
486 user::LoginStatus login_;
487 chromeos::AudioDeviceList output_devices_;
488 chromeos::AudioDeviceList input_devices_;
489 AudioDeviceMap device_map_;
490
491 DISALLOW_COPY_AND_ASSIGN(AudioDetailedView);
492};
493
494} // namespace tray
495
496TrayAudio::TrayAudio(SystemTray* system_tray)
497 : TrayImageItem(system_tray, IDR_AURA_UBER_TRAY_VOLUME_MUTE),
498 volume_view_(NULL),
499 audio_detail_(NULL),
500 pop_up_volume_view_(false) {
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100501 CrasAudioHandler::Get()->AddAudioObserver(this);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100502}
503
504TrayAudio::~TrayAudio() {
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100505 if (CrasAudioHandler::IsInitialized())
506 CrasAudioHandler::Get()->RemoveAudioObserver(this);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100507}
508
509bool TrayAudio::GetInitialVisibility() {
510 return IsAudioMuted();
511}
512
513views::View* TrayAudio::CreateDefaultView(user::LoginStatus status) {
514 volume_view_ = new tray::VolumeView(this, true);
515 return volume_view_;
516}
517
518views::View* TrayAudio::CreateDetailedView(user::LoginStatus status) {
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100519 if (!ash::switches::ShowAudioDeviceMenu() || pop_up_volume_view_) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100520 volume_view_ = new tray::VolumeView(this, false);
521 return volume_view_;
522 } else {
523 audio_detail_ = new tray::AudioDetailedView(this, status);
524 return audio_detail_;
525 }
526}
527
528void TrayAudio::DestroyDefaultView() {
529 volume_view_ = NULL;
530}
531
532void TrayAudio::DestroyDetailedView() {
533 if (audio_detail_) {
534 audio_detail_ = NULL;
535 } else if (volume_view_) {
536 volume_view_ = NULL;
537 pop_up_volume_view_ = false;
538 }
539}
540
541bool TrayAudio::ShouldHideArrow() const {
542 return true;
543}
544
545bool TrayAudio::ShouldShowLauncher() const {
546 return false;
547}
548
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100549void TrayAudio::OnOutputVolumeChanged() {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100550 float percent = GetVolumeLevel();
551 if (tray_view())
552 tray_view()->SetVisible(GetInitialVisibility());
553
554 if (volume_view_) {
555 volume_view_->SetVolumeLevel(percent);
556 SetDetailedViewCloseDelay(kTrayPopupAutoCloseDelayInSeconds);
557 return;
558 }
559 pop_up_volume_view_ = true;
560 PopupDetailedView(kTrayPopupAutoCloseDelayInSeconds, false);
561}
562
563void TrayAudio::OnOutputMuteChanged() {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100564 if (tray_view())
565 tray_view()->SetVisible(GetInitialVisibility());
566
Ben Murdocheb525c52013-07-10 11:40:50 +0100567 if (volume_view_) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100568 volume_view_->Update();
Ben Murdocheb525c52013-07-10 11:40:50 +0100569 SetDetailedViewCloseDelay(kTrayPopupAutoCloseDelayInSeconds);
570 } else {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100571 pop_up_volume_view_ = true;
572 PopupDetailedView(kTrayPopupAutoCloseDelayInSeconds, false);
573 }
574}
575
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100576void TrayAudio::OnInputGainChanged() {
577}
578
579void TrayAudio::OnInputMuteChanged() {
580}
581
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100582void TrayAudio::OnAudioNodesChanged() {
583 Update();
584}
585
586void TrayAudio::OnActiveOutputNodeChanged() {
587 Update();
588}
589
590void TrayAudio::OnActiveInputNodeChanged() {
591 Update();
592}
593
594void TrayAudio::Update() {
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100595 if (tray_view())
596 tray_view()->SetVisible(GetInitialVisibility());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100597 if (audio_detail_)
598 audio_detail_->Update();
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100599 if (volume_view_) {
600 volume_view_->SetVolumeLevel(GetVolumeLevel());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100601 volume_view_->Update();
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100602 }
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100603}
604
605} // namespace internal
606} // namespace ash