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