chromium/ash/birch/birch_model.h

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_BIRCH_BIRCH_MODEL_H_
#define ASH_BIRCH_BIRCH_MODEL_H_

#include <map>
#include <optional>
#include <vector>

#include "ash/ash_export.h"
#include "ash/birch/birch_client.h"
#include "ash/birch/birch_item.h"
#include "ash/public/cpp/session/session_observer.h"
#include "base/functional/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/time/clock.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chromeos/ash/components/geolocation/simple_geolocation_provider.h"
#include "components/prefs/pref_change_registrar.h"

class PrefRegistrySimple;

namespace ash {

class BirchDataProvider;
class BirchIconCache;
class BirchItemRemover;

// Birch model, which is used to aggregate and store relevant information from
// different providers. Both data and prefs are associated with the primary user
// account.
class ASH_EXPORT BirchModel : public SessionObserver,
                              public SimpleGeolocationProvider::Observer {
 public:
  // The callback for lost media data changes. The argument is the updated lost
  // media item.
  using LostMediaDataChangedCallback =
      base::RepeatingCallback<void(std::unique_ptr<BirchItem>)>;

  // BirchModel Observers are notified when the BirchClient has been set.
  class Observer : public base::CheckedObserver {
   public:
    ~Observer() override = default;

    virtual void OnBirchClientSet() = 0;
  };

  BirchModel();
  BirchModel(const BirchModel&) = delete;
  BirchModel& operator=(const BirchModel&) = delete;
  ~BirchModel() override;

  void AddObserver(Observer* observer);
  void RemoveObserver(Observer* observer);

  static void RegisterProfilePrefs(PrefRegistrySimple* registry);

  // Sends a request to the birch keyed service to fetch data into the model.
  // `is_post_login` determines fetch timeout depending on whether this request
  // is made post login.
  // `callback` will run once either all data is fresh or the request timeout
  // has expired.
  void RequestBirchDataFetch(bool is_post_login, base::OnceClosure callback);

  void SetCalendarItems(const std::vector<BirchCalendarItem>& calendar_items);
  void SetAttachmentItems(
      const std::vector<BirchAttachmentItem>& attachment_items);
  void SetFileSuggestItems(
      const std::vector<BirchFileItem>& file_suggest_items);
  void SetRecentTabItems(const std::vector<BirchTabItem>& recent_tab_items);
  void SetLastActiveItems(const std::vector<BirchLastActiveItem>& items);
  void SetMostVisitedItems(const std::vector<BirchMostVisitedItem>& items);
  void SetSelfShareItems(
      const std::vector<BirchSelfShareItem>& self_share_items);
  void SetLostMediaItems(const std::vector<BirchLostMediaItem>& items);
  void SetReleaseNotesItems(
      const std::vector<BirchReleaseNotesItem>& release_notes_items);
  void SetWeatherItems(const std::vector<BirchWeatherItem>& weather_items);
  void SetCoralItems(const std::vector<BirchCoralItem>& coral_items);

  // Sets the BirchClient and begins initializing the BirchItemRemover.
  void SetClientAndInit(BirchClient* client);

  BirchClient* birch_client() { return birch_client_; }
  BirchIconCache* icon_cache() { return icon_cache_.get(); }

  const std::vector<BirchCalendarItem>& GetCalendarItemsForTest() const {
    return calendar_data_.items;
  }

  const std::vector<BirchAttachmentItem>& GetAttachmentItemsForTest() const {
    return attachment_data_.items;
  }
  const std::vector<BirchFileItem>& GetFileSuggestItemsForTest() const {
    return file_suggest_data_.items;
  }
  const std::vector<BirchTabItem>& GetTabsForTest() const {
    return recent_tab_data_.items;
  }
  const std::vector<BirchLastActiveItem>& GetLastActiveItemsForTest() const {
    return last_active_data_.items;
  }
  const std::vector<BirchMostVisitedItem>& GetMostVisitedItemsForTest() const {
    return most_visited_data_.items;
  }
  std::vector<BirchSelfShareItem>& GetSelfShareItemsForTest() {
    return self_share_data_.items;
  }
  std::vector<BirchLostMediaItem>& GetLostMediaItemsForTest() {
    return lost_media_data_.items;
  }
  const std::vector<BirchReleaseNotesItem>& GetReleaseNotesItemsForTest()
      const {
    return release_notes_data_.items;
  }
  const std::vector<BirchWeatherItem>& GetWeatherForTest() const {
    return weather_data_.items;
  }
  std::vector<BirchCoralItem>& GetCoralItemsForTest() {
    return coral_data_.items;
  }

  BirchItemRemover* GetItemRemoverForTest() { return item_remover_.get(); }

  // Returns all items, sorted by ranking. Includes unranked items.
  std::vector<std::unique_ptr<BirchItem>> GetAllItems();

  // Returns all items, sorted by ranking.
  std::vector<std::unique_ptr<BirchItem>> GetItemsForDisplay();

  // Returns whether all data in the model is currently fresh.
  bool IsDataFresh();

  // Add the BirchItem to the list of persistenly removed items.
  void RemoveItem(BirchItem* item);

  void SetLostMediaDataChangedCallback(LostMediaDataChangedCallback callback);

  void ResetLostMediaDataChangedCallback();

  // SessionObserver:
  void OnActiveUserSessionChanged(const AccountId& account_id) override;

  // SimpleGeolocationProvider::Observer:
  void OnGeolocationPermissionChanged(bool enabled) override;

  BirchDataProvider* GetWeatherProviderForTest();
  void OverrideWeatherProviderForTest(
      std::unique_ptr<BirchDataProvider> weather_provider);
  BirchDataProvider* GetCoralProviderForTest();
  void OverrideCoralProviderForTest(
      std::unique_ptr<BirchDataProvider> coral_provider);

  void OverrideClockForTest(base::Clock* clock);
  void SetDataFetchCallbackForTest(base::OnceClosure callback);

 private:
  friend class BirchModelTest;

  // Timer and callback for a pending data fetch request.
  // The callback will be run if the timer expires before all data is fetched.
  struct PendingRequest {
    PendingRequest();
    ~PendingRequest();

    base::OnceClosure callback;
    std::unique_ptr<base::OneShotTimer> timer;
  };

  // Contains information related to fetching and storing data for a single
  // BirchItem type.
  template <typename T>
  struct DataTypeInfo {
    DataTypeInfo(const std::string& pref_name,
                 const std::string& metric_suffix);
    ~DataTypeInfo();

    // Whether a data fetch is in progress.
    bool fetch_in_progress = false;

    // When the fetch for data was started. Used for metrics.
    base::Time fetch_start_time;

    // List of items for this data type.
    std::vector<T> items;

    // Whether the data is fresh.
    bool is_fresh = false;

    // The name of the pref accossiated with this data type.
    std::string pref_name;

    // The suffix for metrics recorded for this data type.
    std::string metric_suffix;

    // The data update request to update existing items.
    std::optional<PendingRequest> update_request;
  };

  template <typename T>
  void SetItems(DataTypeInfo<T>& data_info,
                const std::vector<T>& items,
                bool record_latency);

  // Called when a pending data fetch request timeout expires.
  void HandleRequestTimeout(size_t request_id);

  // Called when the update request completes or timeout expires.
  // `lost_media_data_changed_callback_` will be called with the current lost
  // media items.
  void HandleLostMediaUpdateRequest();

  // Runs data fetch callbacks after a data fetch request when all data items
  // have been refreshed.
  void MaybeRespondToDataFetchRequest();

  // Get current time. The clock may be overridden for testing purposes.
  base::Time GetNow() const;

  // Clears all items.
  void ClearAllItems();

  // Marks all data types as not fresh.
  void MarkDataNotFresh();

  // Initializes the pref change registrars to observe for pref changes.
  void InitPrefChangeRegistrars();

  // Called when a data provider pref changes.
  void OnCalendarPrefChanged();
  void OnFileSuggestPrefChanged();
  void OnChromeTabsPrefChanged();
  void OnLostMediaPrefChanged();
  void OnWeatherPrefChanged();
  void OnReleaseNotesPrefChanged();
  void OnCoralPrefChanged();

  // Records metrics on which providers are hidden based on prefs.
  void RecordProviderHiddenHistograms();

  // Whether `item_remover_` is created and initialized.
  bool IsItemRemoverInitialized();

  // Requests a data fetch from `data_provider` depending on the fetch state.
  template <typename T>
  void StartDataFetchIfNeeded(DataTypeInfo<T>& data_info,
                              BirchDataProvider* data_provider);

  // Returns true if last active items should be included in the results.
  bool ShouldShowLastActive();

  // Returns true if most visited items should be included in the results.
  bool ShouldShowMostVisited();

  // Called when the lost media data provider changed.
  void OnLostMediaDataProviderChanged();

  // Whether this is a post-login fetch (occurring right after login).
  bool is_post_login_fetch_ = false;

  size_t next_request_id_ = 0u;
  // Pending data fetched requests mapped by their request IDs. IDs are
  // generated by incrementing `next_request_id_`.
  std::map<size_t, PendingRequest> pending_requests_;

  // When the last fetch was started. Used for metrics.
  base::Time fetch_start_time_;

  DataTypeInfo<BirchCalendarItem> calendar_data_;
  DataTypeInfo<BirchAttachmentItem> attachment_data_;
  DataTypeInfo<BirchFileItem> file_suggest_data_;
  DataTypeInfo<BirchTabItem> recent_tab_data_;
  DataTypeInfo<BirchLastActiveItem> last_active_data_;
  DataTypeInfo<BirchMostVisitedItem> most_visited_data_;
  DataTypeInfo<BirchSelfShareItem> self_share_data_;
  DataTypeInfo<BirchLostMediaItem> lost_media_data_;
  DataTypeInfo<BirchReleaseNotesItem> release_notes_data_;
  DataTypeInfo<BirchWeatherItem> weather_data_;
  DataTypeInfo<BirchCoralItem> coral_data_;

  raw_ptr<BirchClient> birch_client_ = nullptr;

  std::unique_ptr<BirchIconCache> icon_cache_;

  std::unique_ptr<BirchDataProvider> weather_provider_;
  std::unique_ptr<BirchDataProvider> coral_provider_;

  // When set, this clock is used to ensure a consistent current time is used
  // for testing.
  raw_ptr<base::Clock> clock_override_ = nullptr;

  // Whether an active user session changed notification has been seen. Used to
  // detect the initial notification on signin.
  bool has_active_user_session_changed_ = false;

  PrefChangeRegistrar calendar_pref_registrar_;
  PrefChangeRegistrar file_suggest_pref_registrar_;
  PrefChangeRegistrar chrome_tabs_pref_registrar_;
  PrefChangeRegistrar lost_media_pref_registrar_;
  PrefChangeRegistrar weather_pref_registrar_;
  PrefChangeRegistrar release_notes_pref_registrar_;
  PrefChangeRegistrar coral_pref_registrar_;

  // Used to filter out items which have previously been removed by the user.
  std::unique_ptr<BirchItemRemover> item_remover_;

  // A list of current BirchModel::Observers.
  base::ObserverList<Observer> observers_;

  // Invoked when a data fetch completes.
  base::OnceClosure data_fetch_callback_for_test_;

  // When we last returned a last active item. Used to suppress showing the
  // last active items too often.
  base::Time last_active_last_shown_;

  // When we last returned a most visited item. Used to suppress showing the
  // most visited items too often.
  base::Time most_visited_last_shown_;

  // The callback which runs when the lost media data is changed.
  LostMediaDataChangedCallback lost_media_data_changed_callback_;

  base::WeakPtrFactory<BirchModel> weak_ptr_factory_{this};
};

}  // namespace ash

#endif  // ASH_BIRCH_BIRCH_MODEL_H_