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