chromium/ash/assistant/assistant_notification_expiry_monitor.cc

// Copyright 2019 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/assistant/assistant_notification_expiry_monitor.h"

#include <algorithm>

#include "ash/assistant/assistant_notification_controller_impl.h"
#include "ash/assistant/model/assistant_notification_model.h"
#include "ash/assistant/model/assistant_notification_model_observer.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "chromeos/ash/services/assistant/public/cpp/assistant_service.h"

namespace ash {

namespace {

bool HasExpired(const AssistantNotificationExpiryMonitor::AssistantNotification*
                    notification) {
  return notification->expiry_time.has_value() &&
         (notification->expiry_time.value() <= base::Time::Now());
}

// Returns the minimum of the base::Time instances that actually have a value.
std::optional<base::Time> Min(std::optional<base::Time> left,
                              std::optional<base::Time> right) {
  if (!left.has_value())
    return right;

  if (!right.has_value())
    return left;

  return std::min(left.value(), right.value());
}

}  // namespace

class AssistantNotificationExpiryMonitor::Observer
    : public AssistantNotificationModelObserver {
 public:
  explicit Observer(AssistantNotificationExpiryMonitor* monitor)
      : monitor_(monitor) {}

  Observer(const Observer&) = delete;
  Observer& operator=(const Observer&) = delete;

  ~Observer() override = default;

  void OnNotificationAdded(const AssistantNotification& notification) override {
    monitor_->UpdateTimer();
  }

  void OnNotificationUpdated(
      const AssistantNotification& notification) override {
    monitor_->UpdateTimer();
  }

  void OnNotificationRemoved(const AssistantNotification& notification,
                             bool from_server) override {
    monitor_->UpdateTimer();
  }

  void OnAllNotificationsRemoved(bool from_server) override {
    monitor_->UpdateTimer();
  }

 private:
  const raw_ptr<AssistantNotificationExpiryMonitor> monitor_;
};

AssistantNotificationExpiryMonitor::AssistantNotificationExpiryMonitor(
    AssistantNotificationControllerImpl* controller)
    : controller_(controller), observer_(std::make_unique<Observer>(this)) {
  DCHECK(controller_);
  controller_->model()->AddObserver(observer_.get());
}

AssistantNotificationExpiryMonitor::~AssistantNotificationExpiryMonitor() =
    default;

void AssistantNotificationExpiryMonitor::UpdateTimer() {
  std::optional<base::TimeDelta> timeout = GetTimerTimeout();
  if (timeout) {
    timer_.Start(
        FROM_HERE, timeout.value(),
        base::BindOnce(
            &AssistantNotificationExpiryMonitor::RemoveExpiredNotifications,
            base::Unretained(this)));
  } else {
    timer_.Stop();
  }
}

std::optional<base::TimeDelta>
AssistantNotificationExpiryMonitor::GetTimerTimeout() const {
  std::optional<base::Time> endtime = GetTimerEndTime();
  if (endtime)
    return endtime.value() - base::Time::Now();
  return std::nullopt;
}

std::optional<base::Time> AssistantNotificationExpiryMonitor::GetTimerEndTime()
    const {
  std::optional<base::Time> result = std::nullopt;
  for (const AssistantNotification* notification : GetNotifications())
    result = Min(result, notification->expiry_time);
  return result;
}

void AssistantNotificationExpiryMonitor::RemoveExpiredNotifications() {
  for (const NotificationId& id : GetExpiredNotifications()) {
    VLOG(1) << "Removing expired notification '" << id << "'";
    controller_->RemoveNotificationById(id, /*from_server=*/false);
  }

  UpdateTimer();
}

std::vector<AssistantNotificationExpiryMonitor::NotificationId>
AssistantNotificationExpiryMonitor::GetExpiredNotifications() const {
  std::vector<NotificationId> result;
  for (const AssistantNotification* notification : GetNotifications()) {
    if (HasExpired(notification))
      result.push_back(notification->client_id);
  }
  return result;
}

std::vector<const AssistantNotificationExpiryMonitor::AssistantNotification*>
AssistantNotificationExpiryMonitor::GetNotifications() const {
  return controller_->model()->GetNotifications();
}

}  // namespace ash