OLD | NEW |
| (Empty) |
1 // 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/metrics/user_metrics_recorder.h" | |
12 #include "ash/shell.h" | |
13 #include "ash/system/tray/actionable_view.h" | |
14 #include "ash/system/tray/fixed_sized_scroll_view.h" | |
15 #include "ash/system/tray/hover_highlight_view.h" | |
16 #include "ash/system/tray/system_tray.h" | |
17 #include "ash/system/tray/system_tray_delegate.h" | |
18 #include "ash/system/tray/system_tray_notifier.h" | |
19 #include "ash/system/tray/tray_constants.h" | |
20 #include "ash/volume_control_delegate.h" | |
21 #include "base/strings/utf_string_conversions.h" | |
22 #include "chromeos/audio/cras_audio_handler.h" | |
23 #include "grit/ash_resources.h" | |
24 #include "grit/ash_strings.h" | |
25 #include "third_party/skia/include/core/SkCanvas.h" | |
26 #include "third_party/skia/include/core/SkPaint.h" | |
27 #include "third_party/skia/include/core/SkRect.h" | |
28 #include "third_party/skia/include/effects/SkGradientShader.h" | |
29 #include "ui/base/l10n/l10n_util.h" | |
30 #include "ui/base/resource/resource_bundle.h" | |
31 #include "ui/gfx/canvas.h" | |
32 #include "ui/gfx/font_list.h" | |
33 #include "ui/gfx/image/image.h" | |
34 #include "ui/gfx/image/image_skia_operations.h" | |
35 #include "ui/views/controls/button/image_button.h" | |
36 #include "ui/views/controls/image_view.h" | |
37 #include "ui/views/controls/label.h" | |
38 #include "ui/views/controls/slider.h" | |
39 #include "ui/views/layout/box_layout.h" | |
40 #include "ui/views/view.h" | |
41 | |
42 using chromeos::CrasAudioHandler; | |
43 | |
44 namespace ash { | |
45 namespace internal { | |
46 | |
47 namespace { | |
48 const int kVolumeImageWidth = 25; | |
49 const int kVolumeImageHeight = 25; | |
50 const int kBarSeparatorWidth = 25; | |
51 const int kBarSeparatorHeight = 30; | |
52 const int kSliderRightPaddingToVolumeViewEdge = 17; | |
53 const int kExtraPaddingBetweenBarAndMore = 10; | |
54 | |
55 const int kNoAudioDeviceIcon = -1; | |
56 | |
57 // IDR_AURA_UBER_TRAY_VOLUME_LEVELS contains 5 images, | |
58 // The one for mute is at the 0 index and the other | |
59 // four are used for ascending volume levels. | |
60 const int kVolumeLevels = 4; | |
61 | |
62 bool IsAudioMuted() { | |
63 return CrasAudioHandler::Get()->IsOutputMuted(); | |
64 } | |
65 | |
66 float GetVolumeLevel() { | |
67 return CrasAudioHandler::Get()->GetOutputVolumePercent() / 100.0f; | |
68 } | |
69 | |
70 int GetAudioDeviceIconId(chromeos::AudioDeviceType type) { | |
71 if (type == chromeos::AUDIO_TYPE_HEADPHONE) | |
72 return IDR_AURA_UBER_TRAY_AUDIO_HEADPHONE; | |
73 else if (type == chromeos::AUDIO_TYPE_USB) | |
74 return IDR_AURA_UBER_TRAY_AUDIO_USB; | |
75 else if (type == chromeos::AUDIO_TYPE_BLUETOOTH) | |
76 return IDR_AURA_UBER_TRAY_AUDIO_BLUETOOTH; | |
77 else if (type == chromeos::AUDIO_TYPE_HDMI) | |
78 return IDR_AURA_UBER_TRAY_AUDIO_HDMI; | |
79 else | |
80 return kNoAudioDeviceIcon; | |
81 } | |
82 | |
83 base::string16 GetAudioDeviceName(const chromeos::AudioDevice& device) { | |
84 switch(device.type) { | |
85 case chromeos::AUDIO_TYPE_HEADPHONE: | |
86 return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_HEADPHONE); | |
87 case chromeos::AUDIO_TYPE_INTERNAL_SPEAKER: | |
88 return l10n_util::GetStringUTF16( | |
89 IDS_ASH_STATUS_TRAY_AUDIO_INTERNAL_SPEAKER); | |
90 case chromeos::AUDIO_TYPE_INTERNAL_MIC: | |
91 return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_INTERNAL_MIC); | |
92 case chromeos::AUDIO_TYPE_USB: | |
93 return l10n_util::GetStringFUTF16( | |
94 IDS_ASH_STATUS_TRAY_AUDIO_USB_DEVICE, | |
95 base::UTF8ToUTF16(device.display_name)); | |
96 case chromeos::AUDIO_TYPE_BLUETOOTH: | |
97 return l10n_util::GetStringFUTF16( | |
98 IDS_ASH_STATUS_TRAY_AUDIO_BLUETOOTH_DEVICE, | |
99 base::UTF8ToUTF16(device.display_name)); | |
100 case chromeos::AUDIO_TYPE_HDMI: | |
101 return l10n_util::GetStringFUTF16( | |
102 IDS_ASH_STATUS_TRAY_AUDIO_HDMI_DEVICE, | |
103 base::UTF8ToUTF16(device.display_name)); | |
104 default: | |
105 return base::UTF8ToUTF16(device.display_name); | |
106 } | |
107 } | |
108 | |
109 } // namespace | |
110 | |
111 namespace tray { | |
112 | |
113 class VolumeButton : public views::ToggleImageButton { | |
114 public: | |
115 explicit VolumeButton(views::ButtonListener* listener) | |
116 : views::ToggleImageButton(listener), | |
117 image_index_(-1) { | |
118 SetImageAlignment(ALIGN_CENTER, ALIGN_MIDDLE); | |
119 image_ = ui::ResourceBundle::GetSharedInstance().GetImageNamed( | |
120 IDR_AURA_UBER_TRAY_VOLUME_LEVELS); | |
121 SetPreferredSize(gfx::Size(kTrayPopupItemHeight, kTrayPopupItemHeight)); | |
122 Update(); | |
123 } | |
124 | |
125 virtual ~VolumeButton() {} | |
126 | |
127 void Update() { | |
128 float level = GetVolumeLevel(); | |
129 int image_index = IsAudioMuted() ? | |
130 0 : (level == 1.0 ? | |
131 kVolumeLevels : | |
132 std::max(1, int(std::ceil(level * (kVolumeLevels - 1))))); | |
133 if (image_index != image_index_) { | |
134 gfx::Rect region(0, image_index * kVolumeImageHeight, | |
135 kVolumeImageWidth, kVolumeImageHeight); | |
136 gfx::ImageSkia image_skia = gfx::ImageSkiaOperations::ExtractSubset( | |
137 *(image_.ToImageSkia()), region); | |
138 SetImage(views::CustomButton::STATE_NORMAL, &image_skia); | |
139 image_index_ = image_index; | |
140 } | |
141 SchedulePaint(); | |
142 } | |
143 | |
144 private: | |
145 // Overridden from views::View. | |
146 virtual gfx::Size GetPreferredSize() OVERRIDE { | |
147 gfx::Size size = views::ToggleImageButton::GetPreferredSize(); | |
148 size.set_height(kTrayPopupItemHeight); | |
149 return size; | |
150 } | |
151 | |
152 gfx::Image image_; | |
153 int image_index_; | |
154 | |
155 DISALLOW_COPY_AND_ASSIGN(VolumeButton); | |
156 }; | |
157 | |
158 class VolumeSlider : public views::Slider { | |
159 public: | |
160 explicit VolumeSlider(views::SliderListener* listener) | |
161 : views::Slider(listener, views::Slider::HORIZONTAL) { | |
162 set_focus_border_color(kFocusBorderColor); | |
163 SetValue(GetVolumeLevel()); | |
164 SetAccessibleName( | |
165 ui::ResourceBundle::GetSharedInstance().GetLocalizedString( | |
166 IDS_ASH_STATUS_TRAY_VOLUME)); | |
167 Update(); | |
168 } | |
169 virtual ~VolumeSlider() {} | |
170 | |
171 void Update() { | |
172 UpdateState(!IsAudioMuted()); | |
173 } | |
174 | |
175 DISALLOW_COPY_AND_ASSIGN(VolumeSlider); | |
176 }; | |
177 | |
178 // Vertical bar separator that can be placed on the VolumeView. | |
179 class BarSeparator : public views::View { | |
180 public: | |
181 BarSeparator() {} | |
182 virtual ~BarSeparator() {} | |
183 | |
184 // Overriden from views::View. | |
185 virtual gfx::Size GetPreferredSize() OVERRIDE { | |
186 return gfx::Size(kBarSeparatorWidth, kBarSeparatorHeight); | |
187 } | |
188 | |
189 private: | |
190 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { | |
191 canvas->FillRect(gfx::Rect(width() / 2, 0, 1, height()), | |
192 kButtonStrokeColor); | |
193 } | |
194 | |
195 DISALLOW_COPY_AND_ASSIGN(BarSeparator); | |
196 }; | |
197 | |
198 class VolumeView : public ActionableView, | |
199 public views::ButtonListener, | |
200 public views::SliderListener { | |
201 public: | |
202 VolumeView(SystemTrayItem* owner, bool is_default_view) | |
203 : owner_(owner), | |
204 icon_(NULL), | |
205 slider_(NULL), | |
206 bar_(NULL), | |
207 device_type_(NULL), | |
208 more_(NULL), | |
209 is_default_view_(is_default_view) { | |
210 SetFocusable(false); | |
211 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal, | |
212 kTrayPopupPaddingHorizontal, 0, kTrayPopupPaddingBetweenItems)); | |
213 | |
214 icon_ = new VolumeButton(this); | |
215 AddChildView(icon_); | |
216 | |
217 slider_ = new VolumeSlider(this); | |
218 AddChildView(slider_); | |
219 | |
220 bar_ = new BarSeparator; | |
221 AddChildView(bar_); | |
222 | |
223 device_type_ = new views::ImageView; | |
224 AddChildView(device_type_); | |
225 | |
226 more_ = new views::ImageView; | |
227 more_->EnableCanvasFlippingForRTLUI(true); | |
228 more_->SetImage(ui::ResourceBundle::GetSharedInstance().GetImageNamed( | |
229 IDR_AURA_UBER_TRAY_MORE).ToImageSkia()); | |
230 AddChildView(more_); | |
231 | |
232 Update(); | |
233 } | |
234 | |
235 virtual ~VolumeView() {} | |
236 | |
237 void Update() { | |
238 icon_->Update(); | |
239 slider_->Update(); | |
240 UpdateDeviceTypeAndMore(); | |
241 Layout(); | |
242 } | |
243 | |
244 // Sets volume level on slider_, |percent| is ranged from [0.00] to [1.00]. | |
245 void SetVolumeLevel(float percent) { | |
246 // Slider's value is in finer granularity than audio volume level(0.01), | |
247 // there will be a small discrepancy between slider's value and volume level | |
248 // on audio side. To avoid the jittering in slider UI, do not set change | |
249 // slider value if the change is less than 1%. | |
250 if (std::abs(percent-slider_->value()) < 0.01) | |
251 return; | |
252 // The change in volume will be reflected via accessibility system events, | |
253 // so we prevent the UI event from being sent here. | |
254 slider_->set_enable_accessibility_events(false); | |
255 slider_->SetValue(percent); | |
256 // It is possible that the volume was (un)muted, but the actual volume level | |
257 // did not change. In that case, setting the value of the slider won't | |
258 // trigger an update. So explicitly trigger an update. | |
259 Update(); | |
260 slider_->set_enable_accessibility_events(true); | |
261 } | |
262 | |
263 private: | |
264 // Updates bar_, device_type_ icon, and more_ buttons. | |
265 void UpdateDeviceTypeAndMore() { | |
266 if (!ash::switches::ShowAudioDeviceMenu() || !is_default_view_) { | |
267 more_->SetVisible(false); | |
268 bar_->SetVisible(false); | |
269 device_type_->SetVisible(false); | |
270 return; | |
271 } | |
272 | |
273 CrasAudioHandler* audio_handler = CrasAudioHandler::Get(); | |
274 bool show_more = audio_handler->has_alternative_output() || | |
275 audio_handler->has_alternative_input(); | |
276 more_->SetVisible(show_more); | |
277 | |
278 // Show output device icon if necessary. | |
279 chromeos::AudioDevice device; | |
280 if (!audio_handler->GetActiveOutputDevice(&device)) | |
281 return; | |
282 int device_icon = GetAudioDeviceIconId(device.type); | |
283 bar_->SetVisible(show_more); | |
284 if (device_icon != kNoAudioDeviceIcon) { | |
285 device_type_->SetVisible(true); | |
286 device_type_->SetImage( | |
287 ui::ResourceBundle::GetSharedInstance().GetImageNamed( | |
288 device_icon).ToImageSkia()); | |
289 } else { | |
290 device_type_->SetVisible(false); | |
291 } | |
292 } | |
293 | |
294 void HandleVolumeUp(int volume) { | |
295 CrasAudioHandler* audio_handler = CrasAudioHandler::Get(); | |
296 audio_handler->SetOutputVolumePercent(volume); | |
297 if (audio_handler->IsOutputMuted() && | |
298 !audio_handler->IsOutputVolumeBelowDefaultMuteLvel()) | |
299 audio_handler->SetOutputMute(false); | |
300 } | |
301 | |
302 void HandleVolumeDown(int volume) { | |
303 CrasAudioHandler* audio_handler = CrasAudioHandler::Get(); | |
304 audio_handler->SetOutputVolumePercent(volume); | |
305 if (audio_handler->IsOutputVolumeBelowDefaultMuteLvel() && | |
306 !audio_handler->IsOutputMuted()) { | |
307 audio_handler->SetOutputMute(true); | |
308 } else if (!audio_handler->IsOutputVolumeBelowDefaultMuteLvel() && | |
309 audio_handler->IsOutputMuted()) { | |
310 audio_handler->SetOutputMute(false); | |
311 } | |
312 } | |
313 | |
314 // Overridden from views::View. | |
315 virtual void Layout() OVERRIDE { | |
316 views::View::Layout(); | |
317 | |
318 if (!more_->visible()) { | |
319 int w = width() - slider_->bounds().x() - | |
320 kSliderRightPaddingToVolumeViewEdge; | |
321 slider_->SetSize(gfx::Size(w, slider_->height())); | |
322 return; | |
323 } | |
324 | |
325 // Make sure the chevron always has the full size. | |
326 gfx::Size size = more_->GetPreferredSize(); | |
327 gfx::Rect bounds(size); | |
328 bounds.set_x(width() - size.width() - kTrayPopupPaddingBetweenItems); | |
329 bounds.set_y((height() - size.height()) / 2); | |
330 more_->SetBoundsRect(bounds); | |
331 | |
332 // Layout either bar_ or device_type_ at the left of the more_ button. | |
333 views::View* view_left_to_more; | |
334 if (device_type_->visible()) | |
335 view_left_to_more = device_type_; | |
336 else | |
337 view_left_to_more = bar_; | |
338 gfx::Size view_size = view_left_to_more->GetPreferredSize(); | |
339 gfx::Rect view_bounds(view_size); | |
340 view_bounds.set_x(more_->bounds().x() - view_size.width() - | |
341 kExtraPaddingBetweenBarAndMore); | |
342 view_bounds.set_y((height() - view_size.height()) / 2); | |
343 view_left_to_more->SetBoundsRect(view_bounds); | |
344 | |
345 // Layout vertical bar next to view_left_to_more if device_type_ is visible. | |
346 if (device_type_->visible()) { | |
347 gfx::Size bar_size = bar_->GetPreferredSize(); | |
348 gfx::Rect bar_bounds(bar_size); | |
349 bar_bounds.set_x(view_left_to_more->bounds().x() - bar_size.width()); | |
350 bar_bounds.set_y((height() - bar_size.height()) / 2); | |
351 bar_->SetBoundsRect(bar_bounds); | |
352 } | |
353 | |
354 // Layout slider, calculate slider width. | |
355 gfx::Rect slider_bounds = slider_->bounds(); | |
356 slider_bounds.set_width( | |
357 bar_->bounds().x() | |
358 - (device_type_->visible() ? 0 : kTrayPopupPaddingBetweenItems) | |
359 - slider_bounds.x()); | |
360 slider_->SetBoundsRect(slider_bounds); | |
361 } | |
362 | |
363 // Overridden from views::ButtonListener. | |
364 virtual void ButtonPressed(views::Button* sender, | |
365 const ui::Event& event) OVERRIDE { | |
366 CHECK(sender == icon_); | |
367 bool mute_on = !IsAudioMuted(); | |
368 CrasAudioHandler::Get()->SetOutputMute(mute_on); | |
369 if (!mute_on) | |
370 CrasAudioHandler::Get()->AdjustOutputVolumeToAudibleLevel(); | |
371 } | |
372 | |
373 // Overridden from views:SliderListener. | |
374 virtual void SliderValueChanged(views::Slider* sender, | |
375 float value, | |
376 float old_value, | |
377 views::SliderChangeReason reason) OVERRIDE { | |
378 if (reason == views::VALUE_CHANGED_BY_USER) { | |
379 int volume = value * 100.0f; | |
380 int old_volume = CrasAudioHandler::Get()->GetOutputVolumePercent(); | |
381 // Do not call change audio volume if the difference is less than | |
382 // 1%, which is beyond cras audio api's granularity for output volume. | |
383 if (std::abs(volume - old_volume) < 1) | |
384 return; | |
385 Shell::GetInstance()->metrics()->RecordUserMetricsAction( | |
386 is_default_view_ ? | |
387 ash::UMA_STATUS_AREA_CHANGED_VOLUME_MENU : | |
388 ash::UMA_STATUS_AREA_CHANGED_VOLUME_POPUP); | |
389 if (volume > old_volume) | |
390 HandleVolumeUp(volume); | |
391 else | |
392 HandleVolumeDown(volume); | |
393 } | |
394 icon_->Update(); | |
395 } | |
396 | |
397 // Overriden from ActionableView. | |
398 virtual bool PerformAction(const ui::Event& event) OVERRIDE { | |
399 if (!more_->visible()) | |
400 return false; | |
401 owner_->TransitionDetailedView(); | |
402 return true; | |
403 } | |
404 | |
405 SystemTrayItem* owner_; | |
406 VolumeButton* icon_; | |
407 VolumeSlider* slider_; | |
408 BarSeparator* bar_; | |
409 views::ImageView* device_type_; | |
410 views::ImageView* more_; | |
411 bool is_default_view_; | |
412 | |
413 DISALLOW_COPY_AND_ASSIGN(VolumeView); | |
414 }; | |
415 | |
416 class AudioDetailedView : public TrayDetailsView, | |
417 public ViewClickListener { | |
418 public: | |
419 AudioDetailedView(SystemTrayItem* owner, user::LoginStatus login) | |
420 : TrayDetailsView(owner), | |
421 login_(login) { | |
422 CreateItems(); | |
423 Update(); | |
424 } | |
425 | |
426 virtual ~AudioDetailedView() { | |
427 } | |
428 | |
429 void Update() { | |
430 UpdateAudioDevices(); | |
431 Layout(); | |
432 } | |
433 | |
434 private: | |
435 void CreateItems() { | |
436 CreateScrollableList(); | |
437 CreateHeaderEntry(); | |
438 } | |
439 | |
440 void CreateHeaderEntry() { | |
441 CreateSpecialRow(IDS_ASH_STATUS_TRAY_AUDIO, this); | |
442 } | |
443 | |
444 void UpdateAudioDevices() { | |
445 output_devices_.clear(); | |
446 input_devices_.clear(); | |
447 chromeos::AudioDeviceList devices; | |
448 CrasAudioHandler::Get()->GetAudioDevices(&devices); | |
449 for (size_t i = 0; i < devices.size(); ++i) { | |
450 if (devices[i].is_input) | |
451 input_devices_.push_back(devices[i]); | |
452 else | |
453 output_devices_.push_back(devices[i]); | |
454 } | |
455 UpdateScrollableList(); | |
456 } | |
457 | |
458 void UpdateScrollableList() { | |
459 scroll_content()->RemoveAllChildViews(true); | |
460 device_map_.clear(); | |
461 | |
462 // Add audio output devices. | |
463 AddScrollListInfoItem( | |
464 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_OUTPUT)); | |
465 for (size_t i = 0; i < output_devices_.size(); ++i) { | |
466 HoverHighlightView* container = AddScrollListItem( | |
467 GetAudioDeviceName(output_devices_[i]), | |
468 gfx::Font::NORMAL, | |
469 output_devices_[i].active); /* checkmark if active */ | |
470 device_map_[container] = output_devices_[i]; | |
471 } | |
472 | |
473 AddScrollSeparator(); | |
474 | |
475 // Add audio input devices. | |
476 AddScrollListInfoItem( | |
477 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_AUDIO_INPUT)); | |
478 for (size_t i = 0; i < input_devices_.size(); ++i) { | |
479 HoverHighlightView* container = AddScrollListItem( | |
480 GetAudioDeviceName(input_devices_[i]), | |
481 gfx::Font::NORMAL, | |
482 input_devices_[i].active); /* checkmark if active */ | |
483 device_map_[container] = input_devices_[i]; | |
484 } | |
485 | |
486 scroll_content()->SizeToPreferredSize(); | |
487 scroller()->Layout(); | |
488 } | |
489 | |
490 void AddScrollListInfoItem(const base::string16& text) { | |
491 views::Label* label = new views::Label( | |
492 text, | |
493 ui::ResourceBundle::GetSharedInstance().GetFontList( | |
494 ui::ResourceBundle::BoldFont)); | |
495 | |
496 // Align info item with checkbox items | |
497 int margin = kTrayPopupPaddingHorizontal + | |
498 kTrayPopupDetailsLabelExtraLeftMargin; | |
499 int left_margin = 0; | |
500 int right_margin = 0; | |
501 if (base::i18n::IsRTL()) | |
502 right_margin = margin; | |
503 else | |
504 left_margin = margin; | |
505 | |
506 label->SetBorder( | |
507 views::Border::CreateEmptyBorder(ash::kTrayPopupPaddingBetweenItems, | |
508 left_margin, | |
509 ash::kTrayPopupPaddingBetweenItems, | |
510 right_margin)); | |
511 label->SetHorizontalAlignment(gfx::ALIGN_LEFT); | |
512 label->SetEnabledColor(SkColorSetARGB(192, 0, 0, 0)); | |
513 | |
514 scroll_content()->AddChildView(label); | |
515 } | |
516 | |
517 HoverHighlightView* AddScrollListItem(const base::string16& text, | |
518 gfx::Font::FontStyle style, | |
519 bool checked) { | |
520 HoverHighlightView* container = new HoverHighlightView(this); | |
521 container->AddCheckableLabel(text, style, checked); | |
522 scroll_content()->AddChildView(container); | |
523 return container; | |
524 } | |
525 | |
526 // Overridden from ViewClickListener. | |
527 virtual void OnViewClicked(views::View* sender) OVERRIDE { | |
528 if (sender == footer()->content()) { | |
529 TransitionToDefaultView(); | |
530 } else { | |
531 AudioDeviceMap::iterator iter = device_map_.find(sender); | |
532 if (iter == device_map_.end()) | |
533 return; | |
534 chromeos::AudioDevice& device = iter->second; | |
535 CrasAudioHandler::Get()->SwitchToDevice(device); | |
536 } | |
537 } | |
538 | |
539 typedef std::map<views::View*, chromeos::AudioDevice> AudioDeviceMap; | |
540 | |
541 user::LoginStatus login_; | |
542 chromeos::AudioDeviceList output_devices_; | |
543 chromeos::AudioDeviceList input_devices_; | |
544 AudioDeviceMap device_map_; | |
545 | |
546 DISALLOW_COPY_AND_ASSIGN(AudioDetailedView); | |
547 }; | |
548 | |
549 } // namespace tray | |
550 | |
551 TrayAudio::TrayAudio(SystemTray* system_tray) | |
552 : TrayImageItem(system_tray, IDR_AURA_UBER_TRAY_VOLUME_MUTE), | |
553 volume_view_(NULL), | |
554 audio_detail_(NULL), | |
555 pop_up_volume_view_(false) { | |
556 CrasAudioHandler::Get()->AddAudioObserver(this); | |
557 } | |
558 | |
559 TrayAudio::~TrayAudio() { | |
560 if (CrasAudioHandler::IsInitialized()) | |
561 CrasAudioHandler::Get()->RemoveAudioObserver(this); | |
562 } | |
563 | |
564 bool TrayAudio::GetInitialVisibility() { | |
565 return IsAudioMuted(); | |
566 } | |
567 | |
568 views::View* TrayAudio::CreateDefaultView(user::LoginStatus status) { | |
569 volume_view_ = new tray::VolumeView(this, true); | |
570 return volume_view_; | |
571 } | |
572 | |
573 views::View* TrayAudio::CreateDetailedView(user::LoginStatus status) { | |
574 if (!ash::switches::ShowAudioDeviceMenu() || pop_up_volume_view_) { | |
575 volume_view_ = new tray::VolumeView(this, false); | |
576 return volume_view_; | |
577 } else { | |
578 Shell::GetInstance()->metrics()->RecordUserMetricsAction( | |
579 ash::UMA_STATUS_AREA_DETAILED_AUDIO_VIEW); | |
580 audio_detail_ = new tray::AudioDetailedView(this, status); | |
581 return audio_detail_; | |
582 } | |
583 } | |
584 | |
585 void TrayAudio::DestroyDefaultView() { | |
586 volume_view_ = NULL; | |
587 } | |
588 | |
589 void TrayAudio::DestroyDetailedView() { | |
590 if (audio_detail_) { | |
591 audio_detail_ = NULL; | |
592 } else if (volume_view_) { | |
593 volume_view_ = NULL; | |
594 pop_up_volume_view_ = false; | |
595 } | |
596 } | |
597 | |
598 bool TrayAudio::ShouldHideArrow() const { | |
599 return true; | |
600 } | |
601 | |
602 bool TrayAudio::ShouldShowShelf() const { | |
603 return ash::switches::ShowAudioDeviceMenu() && !pop_up_volume_view_; | |
604 } | |
605 | |
606 void TrayAudio::OnOutputVolumeChanged() { | |
607 float percent = GetVolumeLevel(); | |
608 if (tray_view()) | |
609 tray_view()->SetVisible(GetInitialVisibility()); | |
610 | |
611 if (volume_view_) { | |
612 volume_view_->SetVolumeLevel(percent); | |
613 SetDetailedViewCloseDelay(kTrayPopupAutoCloseDelayInSeconds); | |
614 return; | |
615 } | |
616 pop_up_volume_view_ = true; | |
617 PopupDetailedView(kTrayPopupAutoCloseDelayInSeconds, false); | |
618 } | |
619 | |
620 void TrayAudio::OnOutputMuteChanged() { | |
621 if (tray_view()) | |
622 tray_view()->SetVisible(GetInitialVisibility()); | |
623 | |
624 if (volume_view_) { | |
625 volume_view_->Update(); | |
626 SetDetailedViewCloseDelay(kTrayPopupAutoCloseDelayInSeconds); | |
627 } else { | |
628 pop_up_volume_view_ = true; | |
629 PopupDetailedView(kTrayPopupAutoCloseDelayInSeconds, false); | |
630 } | |
631 } | |
632 | |
633 void TrayAudio::OnInputGainChanged() { | |
634 } | |
635 | |
636 void TrayAudio::OnInputMuteChanged() { | |
637 } | |
638 | |
639 void TrayAudio::OnAudioNodesChanged() { | |
640 Update(); | |
641 } | |
642 | |
643 void TrayAudio::OnActiveOutputNodeChanged() { | |
644 Update(); | |
645 } | |
646 | |
647 void TrayAudio::OnActiveInputNodeChanged() { | |
648 Update(); | |
649 } | |
650 | |
651 void TrayAudio::Update() { | |
652 if (tray_view()) | |
653 tray_view()->SetVisible(GetInitialVisibility()); | |
654 if (audio_detail_) | |
655 audio_detail_->Update(); | |
656 if (volume_view_) { | |
657 volume_view_->SetVolumeLevel(GetVolumeLevel()); | |
658 volume_view_->Update(); | |
659 } | |
660 } | |
661 | |
662 } // namespace internal | |
663 } // namespace ash | |
OLD | NEW |