// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/system/unified/unified_system_tray_model.h"
#include "ash/accessibility/accessibility_controller.h"
#include "ash/shelf/shelf_widget.h"
#include "ash/shell.h"
#include "ash/shell_observer.h"
#include "ash/system/brightness_control_delegate.h"
#include "ash/system/status_area_widget.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "chromeos/dbus/power_manager/backlight.pb.h"
namespace {
// The minimum width for system tray with size of kMedium.
constexpr int kMinWidthMediumSystemTray = 768;
// The maximum width for system tray with size of kMedium.
constexpr int kMaxWidthMediumSystemTray = 1280;
} // namespace
namespace ash {
class UnifiedSystemTrayModel::DBusObserver
: public chromeos::PowerManagerClient::Observer {
public:
explicit DBusObserver(UnifiedSystemTrayModel* owner);
DBusObserver(const DBusObserver&) = delete;
DBusObserver& operator=(const DBusObserver&) = delete;
~DBusObserver() override;
private:
void HandleInitialBrightness(std::optional<double> percent);
// chromeos::PowerManagerClient::Observer:
void ScreenBrightnessChanged(
const power_manager::BacklightBrightnessChange& change) override;
void KeyboardBrightnessChanged(
const power_manager::BacklightBrightnessChange& change) override;
const raw_ptr<UnifiedSystemTrayModel> owner_;
base::WeakPtrFactory<DBusObserver> weak_ptr_factory_{this};
};
UnifiedSystemTrayModel::DBusObserver::DBusObserver(
UnifiedSystemTrayModel* owner)
: owner_(owner) {
chromeos::PowerManagerClient::Get()->AddObserver(this);
Shell::Get()->brightness_control_delegate()->GetBrightnessPercent(
base::BindOnce(&DBusObserver::HandleInitialBrightness,
weak_ptr_factory_.GetWeakPtr()));
}
UnifiedSystemTrayModel::DBusObserver::~DBusObserver() {
chromeos::PowerManagerClient::Get()->RemoveObserver(this);
}
void UnifiedSystemTrayModel::DBusObserver::HandleInitialBrightness(
std::optional<double> percent) {
if (percent.has_value())
owner_->DisplayBrightnessChanged(percent.value() / 100.,
false /* by_user */);
}
void UnifiedSystemTrayModel::DBusObserver::ScreenBrightnessChanged(
const power_manager::BacklightBrightnessChange& change) {
owner_->DisplayBrightnessChanged(
change.percent() / 100.,
change.cause() ==
power_manager::BacklightBrightnessChange_Cause_USER_REQUEST);
}
void UnifiedSystemTrayModel::DBusObserver::KeyboardBrightnessChanged(
const power_manager::BacklightBrightnessChange& change) {
owner_->KeyboardBrightnessChanged(change.percent() / 100., change.cause());
}
UnifiedSystemTrayModel::UnifiedSystemTrayModel(Shelf* shelf)
: shelf_(shelf), dbus_observer_(std::make_unique<DBusObserver>(this)) {
// |shelf_| might be null in unit tests.
pagination_model_ = std::make_unique<PaginationModel>(
shelf_ ? shelf_->GetStatusAreaWidget()->GetRootView() : nullptr);
}
UnifiedSystemTrayModel::~UnifiedSystemTrayModel() = default;
void UnifiedSystemTrayModel::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void UnifiedSystemTrayModel::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
std::optional<bool> UnifiedSystemTrayModel::GetNotificationExpanded(
const std::string& notification_id) const {
auto it = notification_changes_.find(notification_id);
return it == notification_changes_.end() ? std::optional<bool>()
: std::optional<bool>(it->second);
}
void UnifiedSystemTrayModel::SetTargetNotification(
const std::string& notification_id) {
DCHECK(!notification_id.empty());
notification_target_id_ = notification_id;
notification_target_mode_ = NotificationTargetMode::NOTIFICATION_ID;
}
void UnifiedSystemTrayModel::SetNotificationExpanded(
const std::string& notification_id,
bool expanded) {
notification_changes_[notification_id] = expanded;
}
void UnifiedSystemTrayModel::RemoveNotificationExpanded(
const std::string& notification_id) {
notification_changes_.erase(notification_id);
}
void UnifiedSystemTrayModel::ClearNotificationChanges() {
notification_changes_.clear();
}
UnifiedSystemTrayModel::SystemTrayButtonSize
UnifiedSystemTrayModel::GetSystemTrayButtonSize() const {
// |shelf_| might be null in unit tests, returns medium size as default.
if (!shelf_)
return SystemTrayButtonSize::kMedium;
// Handles the cases: the shelf is placed horizontally or vertically, or the
// screen is rotated.
const int display_size =
std::max(GetDisplay().size().width(), GetDisplay().size().height());
if (display_size < kMinWidthMediumSystemTray)
return SystemTrayButtonSize::kSmall;
if (display_size <= kMaxWidthMediumSystemTray)
return SystemTrayButtonSize::kMedium;
return SystemTrayButtonSize::kLarge;
}
void UnifiedSystemTrayModel::DisplayBrightnessChanged(float brightness,
bool by_user) {
display_brightness_ = brightness;
for (auto& observer : observers_)
observer.OnDisplayBrightnessChanged(by_user);
}
void UnifiedSystemTrayModel::KeyboardBrightnessChanged(
float brightness,
power_manager::BacklightBrightnessChange_Cause cause) {
keyboard_brightness_ = brightness;
for (auto& observer : observers_)
observer.OnKeyboardBrightnessChanged(cause);
}
const display::Display UnifiedSystemTrayModel::GetDisplay() const {
// |shelf_| might be null in unit tests, returns primary display as default.
if (!shelf_)
return display::Screen::GetScreen()->GetPrimaryDisplay();
return display::Screen::GetScreen()->GetDisplayNearestWindow(
shelf_->GetStatusAreaWidget()
->GetRootView()
->GetWidget()
->GetNativeWindow());
}
} // namespace ash