1a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
2a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// found in the LICENSE file.
4a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
5a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ash/system/audio/volume_view.h"
6a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
7a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ash/ash_constants.h"
8a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ash/shell.h"
9a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ash/system/audio/tray_audio.h"
10a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ash/system/audio/tray_audio_delegate.h"
11a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ash/system/tray/system_tray_item.h"
12a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ash/system/tray/tray_constants.h"
13a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "grit/ash_resources.h"
14a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "grit/ash_strings.h"
15a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/base/resource/resource_bundle.h"
16a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/gfx/canvas.h"
17a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/gfx/image/image_skia_operations.h"
18a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/views/controls/button/image_button.h"
19a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/views/controls/image_view.h"
20a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/views/layout/box_layout.h"
21a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
22a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)namespace {
23a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const int kVolumeImageWidth = 25;
24a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const int kVolumeImageHeight = 25;
25a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const int kBarSeparatorWidth = 25;
26a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const int kBarSeparatorHeight = 30;
27a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const int kSliderRightPaddingToVolumeViewEdge = 17;
28a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const int kExtraPaddingBetweenBarAndMore = 10;
29a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
30a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// IDR_AURA_UBER_TRAY_VOLUME_LEVELS contains 5 images,
31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// The one for mute is at the 0 index and the other
32a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// four are used for ascending volume levels.
33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const int kVolumeLevels = 4;
34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}  // namespace
36a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)namespace ash {
38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)namespace tray {
39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
40a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)class VolumeButton : public views::ToggleImageButton {
41a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) public:
42a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)   VolumeButton(views::ButtonListener* listener,
43a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                system::TrayAudioDelegate* audio_delegate)
44a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      : views::ToggleImageButton(listener),
45a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        audio_delegate_(audio_delegate),
46a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        image_index_(-1) {
47a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    SetImageAlignment(ALIGN_CENTER, ALIGN_MIDDLE);
48a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    image_ = ui::ResourceBundle::GetSharedInstance().GetImageNamed(
49a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        IDR_AURA_UBER_TRAY_VOLUME_LEVELS);
50a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    Update();
51a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
52a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
53a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  virtual ~VolumeButton() {}
54a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
55a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  void Update() {
56a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    float level =
57a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        static_cast<float>(audio_delegate_->GetOutputVolumeLevel()) / 100.0f;
58a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    int image_index = audio_delegate_->IsOutputAudioMuted() ?
59a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        0 : (level == 1.0 ?
60a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)             kVolumeLevels :
61a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)             std::max(1, int(std::ceil(level * (kVolumeLevels - 1)))));
62a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (image_index != image_index_) {
63a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      gfx::Rect region(0, image_index * kVolumeImageHeight,
64a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                       kVolumeImageWidth, kVolumeImageHeight);
65a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      gfx::ImageSkia image_skia = gfx::ImageSkiaOperations::ExtractSubset(
66a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          *(image_.ToImageSkia()), region);
67a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      SetImage(views::CustomButton::STATE_NORMAL, &image_skia);
68a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      image_index_ = image_index;
69a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
70a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    SchedulePaint();
71a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
72a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
73a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private:
74a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Overridden from views::View.
75cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  virtual gfx::Size GetPreferredSize() const OVERRIDE {
76a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    gfx::Size size = views::ToggleImageButton::GetPreferredSize();
77a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    size.set_height(kTrayPopupItemHeight);
78a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return size;
79a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
80a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
81a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  system::TrayAudioDelegate* audio_delegate_;
82a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  gfx::Image image_;
83a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  int image_index_;
84a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
85a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(VolumeButton);
86a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)};
87a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
88a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)class VolumeSlider : public views::Slider {
89a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) public:
90a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  VolumeSlider(views::SliderListener* listener,
91a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)               system::TrayAudioDelegate* audio_delegate)
92a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      : views::Slider(listener, views::Slider::HORIZONTAL),
93a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        audio_delegate_(audio_delegate) {
94a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    set_focus_border_color(kFocusBorderColor);
95a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    SetValue(
96a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        static_cast<float>(audio_delegate_->GetOutputVolumeLevel()) / 100.0f);
97a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    SetAccessibleName(
98a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                IDS_ASH_STATUS_TRAY_VOLUME));
100a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    Update();
101a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
102a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  virtual ~VolumeSlider() {}
103a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
104a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  void Update() {
105a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    UpdateState(!audio_delegate_->IsOutputAudioMuted());
106a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
107a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
108a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private:
109a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  system::TrayAudioDelegate* audio_delegate_;
110a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
111a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(VolumeSlider);
112a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)};
113a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
114a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Vertical bar separator that can be placed on the VolumeView.
115a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)class BarSeparator : public views::View {
116a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) public:
117a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  BarSeparator() {}
118a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  virtual ~BarSeparator() {}
119a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
120a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Overriden from views::View.
121cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  virtual gfx::Size GetPreferredSize() const OVERRIDE {
122a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return gfx::Size(kBarSeparatorWidth, kBarSeparatorHeight);
123a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
124a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
125a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) private:
126a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
127a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    canvas->FillRect(gfx::Rect(width() / 2, 0, 1, height()),
128a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                     kButtonStrokeColor);
129a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
130a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
131a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(BarSeparator);
132a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)};
133a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
134a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)VolumeView::VolumeView(SystemTrayItem* owner,
135a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                       system::TrayAudioDelegate* audio_delegate,
136a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                       bool is_default_view)
137a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    : owner_(owner),
138a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      audio_delegate_(audio_delegate),
139a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      icon_(NULL),
140a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      slider_(NULL),
141a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      bar_(NULL),
142a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      device_type_(NULL),
143a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      more_(NULL),
144a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      is_default_view_(is_default_view) {
145a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  SetFocusable(false);
146a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal,
147a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        kTrayPopupPaddingHorizontal, 0, kTrayPopupPaddingBetweenItems));
148a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
149a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  icon_ = new VolumeButton(this, audio_delegate_);
150a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  AddChildView(icon_);
151a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
152a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  slider_ = new VolumeSlider(this, audio_delegate_);
153a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  AddChildView(slider_);
154a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
155a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  bar_ = new BarSeparator;
156a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  AddChildView(bar_);
157a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
158a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  device_type_ = new views::ImageView;
159a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  AddChildView(device_type_);
160a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
161a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  more_ = new views::ImageView;
162a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  more_->EnableCanvasFlippingForRTLUI(true);
163a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  more_->SetImage(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
164a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      IDR_AURA_UBER_TRAY_MORE).ToImageSkia());
165a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  AddChildView(more_);
166a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
167a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  Update();
168a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
169a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
170a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)VolumeView::~VolumeView() {
171a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
172a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
173a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void VolumeView::Update() {
174a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  icon_->Update();
175a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  slider_->Update();
176a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  UpdateDeviceTypeAndMore();
177a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  Layout();
178a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
179a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
180a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void VolumeView::SetVolumeLevel(float percent) {
181a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Slider's value is in finer granularity than audio volume level(0.01),
182a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // there will be a small discrepancy between slider's value and volume level
183a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // on audio side. To avoid the jittering in slider UI, do not set change
184a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // slider value if the change is less than 1%.
185a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (std::abs(percent-slider_->value()) < 0.01)
186a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return;
187a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // The change in volume will be reflected via accessibility system events,
188a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // so we prevent the UI event from being sent here.
189a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  slider_->set_enable_accessibility_events(false);
190a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  slider_->SetValue(percent);
191a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // It is possible that the volume was (un)muted, but the actual volume level
192a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // did not change. In that case, setting the value of the slider won't
193a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // trigger an update. So explicitly trigger an update.
194a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  Update();
195a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  slider_->set_enable_accessibility_events(true);
196a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
197a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
198a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void VolumeView::UpdateDeviceTypeAndMore() {
199a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!TrayAudio::ShowAudioDeviceMenu() || !is_default_view_) {
200a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    more_->SetVisible(false);
201a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    bar_->SetVisible(false);
202a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    device_type_->SetVisible(false);
203a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return;
204a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
205a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
206a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  bool show_more = audio_delegate_->HasAlternativeSources();
207a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  more_->SetVisible(show_more);
208a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  bar_->SetVisible(show_more);
209a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
210a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Show output device icon if necessary.
211a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  int device_icon = audio_delegate_->GetActiveOutputDeviceIconId();
212a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (device_icon != system::TrayAudioDelegate::kNoAudioDeviceIcon) {
213a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    device_type_->SetVisible(true);
214a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    device_type_->SetImage(
215a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        ui::ResourceBundle::GetSharedInstance().GetImageNamed(
216a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            device_icon).ToImageSkia());
217a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  } else {
218a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    device_type_->SetVisible(false);
219a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
220a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
221a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
222a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void VolumeView::HandleVolumeUp(float level) {
223a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  audio_delegate_->SetOutputVolumeLevel(level);
224a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (audio_delegate_->IsOutputAudioMuted() &&
225a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      level > audio_delegate_->GetOutputDefaultVolumeMuteLevel()) {
226a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    audio_delegate_->SetOutputAudioIsMuted(false);
227a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
228a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
229a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
230a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void VolumeView::HandleVolumeDown(float level) {
231a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  audio_delegate_->SetOutputVolumeLevel(level);
232a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!audio_delegate_->IsOutputAudioMuted() &&
233a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      level <= audio_delegate_->GetOutputDefaultVolumeMuteLevel()) {
234a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    audio_delegate_->SetOutputAudioIsMuted(true);
235a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  } else if (audio_delegate_->IsOutputAudioMuted() &&
236a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)             level > audio_delegate_->GetOutputDefaultVolumeMuteLevel()) {
237a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    audio_delegate_->SetOutputAudioIsMuted(false);
238a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
239a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
240a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
241a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void VolumeView::Layout() {
242a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  views::View::Layout();
243a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
244a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!more_->visible()) {
245a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    int w = width() - slider_->bounds().x() -
246a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            kSliderRightPaddingToVolumeViewEdge;
247a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    slider_->SetSize(gfx::Size(w, slider_->height()));
248a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return;
249a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
250a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
251a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Make sure the chevron always has the full size.
252a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  gfx::Size size = more_->GetPreferredSize();
253a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  gfx::Rect bounds(size);
254a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  bounds.set_x(width() - size.width() - kTrayPopupPaddingBetweenItems);
255a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  bounds.set_y((height() - size.height()) / 2);
256a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  more_->SetBoundsRect(bounds);
257a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
258a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Layout either bar_ or device_type_ at the left of the more_ button.
259a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  views::View* view_left_to_more;
260a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (device_type_->visible())
261a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    view_left_to_more = device_type_;
262a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  else
263a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    view_left_to_more = bar_;
264a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  gfx::Size view_size = view_left_to_more->GetPreferredSize();
265a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  gfx::Rect view_bounds(view_size);
266a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  view_bounds.set_x(more_->bounds().x() - view_size.width() -
267a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                    kExtraPaddingBetweenBarAndMore);
268a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  view_bounds.set_y((height() - view_size.height()) / 2);
269a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  view_left_to_more->SetBoundsRect(view_bounds);
270a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
271a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Layout vertical bar next to view_left_to_more if device_type_ is visible.
272a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (device_type_->visible()) {
273a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    gfx::Size bar_size = bar_->GetPreferredSize();
274a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    gfx::Rect bar_bounds(bar_size);
275a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    bar_bounds.set_x(view_left_to_more->bounds().x() - bar_size.width());
276a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    bar_bounds.set_y((height() - bar_size.height()) / 2);
277a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    bar_->SetBoundsRect(bar_bounds);
278a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
279a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
280a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Layout slider, calculate slider width.
281a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  gfx::Rect slider_bounds = slider_->bounds();
282a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  slider_bounds.set_width(
283a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      bar_->bounds().x()
284a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      - (device_type_->visible() ? 0 : kTrayPopupPaddingBetweenItems)
285a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      - slider_bounds.x());
286a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  slider_->SetBoundsRect(slider_bounds);
287a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
288a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
289a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void VolumeView::ButtonPressed(views::Button* sender, const ui::Event& event) {
290a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  CHECK(sender == icon_);
291a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  bool mute_on = !audio_delegate_->IsOutputAudioMuted();
292a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  audio_delegate_->SetOutputAudioIsMuted(mute_on);
293a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!mute_on)
294a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    audio_delegate_->AdjustOutputVolumeToAudibleLevel();
295a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  icon_->Update();
296a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
297a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
298a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void VolumeView::SliderValueChanged(views::Slider* sender,
299a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                    float value,
300a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                    float old_value,
301a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                    views::SliderChangeReason reason) {
302a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (reason == views::VALUE_CHANGED_BY_USER) {
303a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    float new_volume = value * 100.0f;
304a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    float current_volume = audio_delegate_->GetOutputVolumeLevel();
305a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // Do not call change audio volume if the difference is less than
306a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // 1%, which is beyond cras audio api's granularity for output volume.
307a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (std::abs(new_volume - current_volume) < 1.0f)
308a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return;
309a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    Shell::GetInstance()->metrics()->RecordUserMetricsAction(
310a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        is_default_view_ ?
311a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        ash::UMA_STATUS_AREA_CHANGED_VOLUME_MENU :
312a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        ash::UMA_STATUS_AREA_CHANGED_VOLUME_POPUP);
313a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (new_volume > current_volume)
314a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      HandleVolumeUp(new_volume);
315a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    else
316a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      HandleVolumeDown(new_volume);
317a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
318a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  icon_->Update();
319a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
320a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
321a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool VolumeView::PerformAction(const ui::Event& event) {
322a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!more_->visible())
323a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return false;
324a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  owner_->TransitionDetailedView();
325a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return true;
326a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
327a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
328a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}  // namespace tray
329a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}  // namespace ash
330