blob: 5d968585b30f71a5394a832c3d20e96954a50a5d [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/system/audio/tray_audio.h"
#include "ash/metrics/user_metrics_recorder.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "ash/system/audio/audio_detailed_view.h"
#include "ash/system/audio/volume_view.h"
#include "ash/system/tray/system_tray.h"
#include "ash/system/tray/tray_constants.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "ui/display/display.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/manager/managed_display_info.h"
#include "ui/display/screen.h"
#include "ui/views/view.h"
namespace ash {
using chromeos::CrasAudioHandler;
using chromeos::DBusThreadManager;
TrayAudio::TrayAudio(SystemTray* system_tray)
: TrayImageItem(system_tray, kSystemTrayVolumeMuteIcon, UMA_AUDIO),
volume_view_(nullptr),
pop_up_volume_view_(false),
audio_detail_view_(nullptr) {
if (CrasAudioHandler::IsInitialized())
CrasAudioHandler::Get()->AddAudioObserver(this);
display::Screen::GetScreen()->AddObserver(this);
DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
}
TrayAudio::~TrayAudio() {
DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
display::Screen::GetScreen()->RemoveObserver(this);
if (CrasAudioHandler::IsInitialized())
CrasAudioHandler::Get()->RemoveAudioObserver(this);
}
// static
void TrayAudio::ShowPopUpVolumeView() {
// Show the popup on all monitors with a system tray.
for (RootWindowController* root : Shell::GetAllRootWindowControllers()) {
SystemTray* system_tray = root->GetSystemTray();
if (!system_tray)
continue;
// Show the popup by simulating a volume change. The provided node id and
// volume value are ignored.
system_tray->GetTrayAudio()->OnOutputNodeVolumeChanged(0, 0);
}
}
bool TrayAudio::GetInitialVisibility() {
return CrasAudioHandler::Get()->IsOutputMuted();
}
views::View* TrayAudio::CreateDefaultView(LoginStatus status) {
volume_view_ = new tray::VolumeView(this, true);
return volume_view_;
}
views::View* TrayAudio::CreateDetailedView(LoginStatus status) {
if (pop_up_volume_view_) {
volume_view_ = new tray::VolumeView(this, false);
return volume_view_;
} else {
Shell::Get()->metrics()->RecordUserMetricsAction(
UMA_STATUS_AREA_DETAILED_AUDIO_VIEW);
audio_detail_view_ = new tray::AudioDetailedView(this);
return audio_detail_view_;
}
}
void TrayAudio::OnDefaultViewDestroyed() {
volume_view_ = nullptr;
}
void TrayAudio::OnDetailedViewDestroyed() {
if (audio_detail_view_) {
audio_detail_view_ = nullptr;
} else if (volume_view_) {
volume_view_ = nullptr;
pop_up_volume_view_ = false;
}
}
bool TrayAudio::ShouldShowShelf() const {
return !pop_up_volume_view_;
}
void TrayAudio::OnOutputNodeVolumeChanged(uint64_t /* node_id */,
int /* volume */) {
float percent = CrasAudioHandler::Get()->GetOutputVolumePercent() / 100.0f;
if (tray_view())
tray_view()->SetVisible(GetInitialVisibility());
if (volume_view_) {
volume_view_->SetVolumeLevel(percent);
SetDetailedViewCloseDelay(kTrayPopupAutoCloseDelayInSeconds);
return;
}
pop_up_volume_view_ = true;
ShowDetailedView(kTrayPopupAutoCloseDelayInSeconds);
}
void TrayAudio::OnOutputMuteChanged(bool /* mute_on */, bool system_adjust) {
if (tray_view())
tray_view()->SetVisible(GetInitialVisibility());
if (volume_view_) {
volume_view_->Update();
SetDetailedViewCloseDelay(kTrayPopupAutoCloseDelayInSeconds);
} else if (!system_adjust) {
pop_up_volume_view_ = true;
ShowDetailedView(kTrayPopupAutoCloseDelayInSeconds);
}
}
void TrayAudio::OnAudioNodesChanged() {
Update();
}
void TrayAudio::OnActiveOutputNodeChanged() {
Update();
}
void TrayAudio::OnActiveInputNodeChanged() {
Update();
}
void TrayAudio::ChangeInternalSpeakerChannelMode() {
// Swap left/right channel only if it is in Yoga mode.
bool swap = false;
if (display::Display::HasInternalDisplay()) {
const display::ManagedDisplayInfo& display_info =
Shell::Get()->display_manager()->GetDisplayInfo(
display::Display::InternalDisplayId());
if (display_info.GetActiveRotation() == display::Display::ROTATE_180)
swap = true;
}
CrasAudioHandler::Get()->SwapInternalSpeakerLeftRightChannel(swap);
}
void TrayAudio::OnDisplayAdded(const display::Display& new_display) {
if (!new_display.IsInternal())
return;
ChangeInternalSpeakerChannelMode();
// This event will be triggered when the lid of the device is opened to exit
// the docked mode, we should always start or re-start HDMI re-discovering
// grace period right after this event.
CrasAudioHandler::Get()->SetActiveHDMIOutoutRediscoveringIfNecessary(true);
}
void TrayAudio::OnDisplayRemoved(const display::Display& old_display) {
if (!old_display.IsInternal())
return;
ChangeInternalSpeakerChannelMode();
// This event will be triggered when the lid of the device is closed to enter
// the docked mode, we should always start or re-start HDMI re-discovering
// grace period right after this event.
CrasAudioHandler::Get()->SetActiveHDMIOutoutRediscoveringIfNecessary(true);
}
void TrayAudio::OnDisplayMetricsChanged(const display::Display& display,
uint32_t changed_metrics) {
if (!display.IsInternal())
return;
if (changed_metrics & display::DisplayObserver::DISPLAY_METRIC_ROTATION)
ChangeInternalSpeakerChannelMode();
// The event could be triggered multiple times during the HDMI display
// transition, we don't need to restart HDMI re-discovering grace period
// it is already started earlier.
CrasAudioHandler::Get()->SetActiveHDMIOutoutRediscoveringIfNecessary(false);
}
void TrayAudio::SuspendDone(const base::TimeDelta& sleep_duration) {
// This event is triggered when the device resumes after earlier suspension,
// we should always start or re-start HDMI re-discovering
// grace period right after this event.
CrasAudioHandler::Get()->SetActiveHDMIOutoutRediscoveringIfNecessary(true);
}
void TrayAudio::Update() {
if (tray_view())
tray_view()->SetVisible(GetInitialVisibility());
if (volume_view_) {
volume_view_->SetVolumeLevel(
CrasAudioHandler::Get()->GetOutputVolumePercent() / 100.0f);
volume_view_->Update();
}
if (audio_detail_view_)
audio_detail_view_->Update();
}
} // namespace ash