chromium/ash/system/unified/unified_system_tray_model.cc

// 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