chromium/ash/birch/birch_item.h

// Copyright 2024 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_ITEM_H_
#define ASH_BIRCH_BIRCH_ITEM_H_

#include <string>

#include "ash/ash_export.h"
#include "base/files/file_path.h"
#include "base/functional/callback_forward.h"
#include "base/time/time.h"
#include "base/unguessable_token.h"
#include "ui/base/models/image_model.h"
#include "url/gurl.h"

class PrefRegistrySimple;

namespace ash {

// These values are used in metrics and should not be reordered or deleted.
// If you are adding to this enum, please keep in sync with
// tools/metrics/histograms/metadata/ash/enums.xml as well as metrics in
// tools/metrics/histograms/metadata/ash/histograms.xml
enum class BirchItemType {
  kTest = 0,          // Internal type used for testing.
  kCalendar = 1,      // Calendar event.
  kAttachment = 2,    // File attachment from calendar event.
  kFile = 3,          // File suggestion e.g. Google Drive file.
  kTab = 4,           // Recent tab from other device.
  kWeather = 5,       // Weather conditions.
  kReleaseNotes = 6,  // Release notes from recent OS update.
  kSelfShare = 7,     // Tabs shared to self from ChromeSync API.
  kMostVisited = 8,   // Most frequently visited URLs.
  kLastActive = 9,    // Last active URL.
  kLostMedia = 10,    // Tab that is currently playing media.
  kCoral = 11,        // Coral provider.
  kMaxValue = kCoral,
};

// These values are used to determine which secondary icon to load for the items
// that contain secondary icons.
enum class SecondaryIconType {
  kTabFromDesktop,            // Type that links to desktop icon.
  kTabFromPhone,              // Type that links to phone/portrait icon.
  kTabFromTablet,             // Type that links to tablet/landscape icon.
  kTabFromUnknown,            // Type that links to question-mark icon.
  kLostMediaAudio,            // Type that links to audio icon.
  kLostMediaVideo,            // Type that links to media icon.
  kLostMediaVideoConference,  // Type that links to video conference icon.
  kNoIcon,                    // Type where we will not load a secondary icon.
  kMaxValue = kNoIcon,
};

// These values are used to determine the types of chip add-ons which is an
// additional UI component like the join button of calendar item.
enum class BirchAddonType {
  kNone,    // No add-ons.
  kButton,  // A button with an action, e,g. the calendar join button.
  kWeatherTempLabelF,  // A label for weather temperature in Fahrenheit.
  kWeatherTempLabelC,  // A label for weather temperature in Celsius.
};

// The base item which is stored by the birch model.
class ASH_EXPORT BirchItem {
 public:
  BirchItem(const std::u16string& title, const std::u16string& subtitle);
  BirchItem(BirchItem&&);
  BirchItem& operator=(BirchItem&&);
  BirchItem(const BirchItem&);
  BirchItem& operator=(const BirchItem&);
  virtual ~BirchItem();
  bool operator==(const BirchItem& rhs) const;

  static void RegisterProfilePrefs(PrefRegistrySimple* registry);

  virtual BirchItemType GetType() const = 0;

  // Print the item to a string for debugging. The format is not stable.
  virtual std::string ToString() const = 0;

  // Perform the action associated with this item (e.g. open a document).
  virtual void PerformAction() = 0;

  // Loads the icon for this image. This may invoke the callback immediately
  // (e.g. with a local icon) or there may be a delay for a network fetch.
  // The `SecondaryIconType` passed to `BirchChipButton` allows the view to set
  // a corresponding secondary icon image.
  using LoadIconCallback =
      base::OnceCallback<void(const ui::ImageModel&, SecondaryIconType)>;
  virtual void LoadIcon(LoadIconCallback callback) const = 0;

  // Records metrics when the user takes an action on the item (e.g. clicks or
  // taps on it).
  void RecordActionMetrics();

  virtual std::u16string GetAccessibleName() const;

  // Performs the action associated with the add-on of this item (e.g. joining a
  // meeting for Calendar). When the add-on action is available, `addon_label()`
  // will be set to the user-friendly action name.
  virtual void PerformAddonAction();
  virtual BirchAddonType GetAddonType() const;
  virtual std::u16string GetAddonAccessibleName() const;

  const std::u16string& title() const { return title_; }
  const std::u16string& subtitle() const { return subtitle_; }

  void set_ranking(float ranking) { ranking_ = ranking; }
  float ranking() const { return ranking_; }

  std::optional<std::u16string> addon_label() const { return addon_label_; }

  static void set_action_count_for_test(int value) { action_count_ = value; }

 protected:
  void set_addon_label(const std::u16string& addon_label) {
    addon_label_ = addon_label;
  }

 private:
  // The title to be displayed in birch chip UI.
  std::u16string title_;

  // The subtitle to be displayed in birch chip UI.
  std::u16string subtitle_;

  // The label for add-on component of the chip, e.g. "Join" on calendar join
  // button.
  std::optional<std::u16string> addon_label_;

  float ranking_;  // Lower is better.

  // Clicks or taps on birch chips, across this login session. Used for metrics.
  static int action_count_;
};

// A birch item which contains calendar event information.
class ASH_EXPORT BirchCalendarItem : public BirchItem {
 public:
  // Used for ranking calendar items, `kAccepted` has the highest priority.
  enum class ResponseStatus {
    kAccepted = 0,
    kTentative = 1,
    kNeedsAction = 2,
    kDeclined = 3
  };

  BirchCalendarItem(const std::u16string& title,
                    const base::Time& start_time,
                    const base::Time& end_time,
                    const GURL& calendar_url,
                    const GURL& conference_url,
                    const std::string& event_id,
                    const bool all_day_event,
                    ResponseStatus response_status = ResponseStatus::kAccepted);
  BirchCalendarItem(BirchCalendarItem&&);
  BirchCalendarItem(const BirchCalendarItem&);
  BirchCalendarItem& operator=(const BirchCalendarItem&);
  ~BirchCalendarItem() override;

  // BirchItem:
  BirchItemType GetType() const override;
  std::string ToString() const override;
  void PerformAction() override;
  void PerformAddonAction() override;
  void LoadIcon(LoadIconCallback callback) const override;
  BirchAddonType GetAddonType() const override;
  std::u16string GetAddonAccessibleName() const override;

  const base::Time& start_time() const { return start_time_; }
  const base::Time& end_time() const { return end_time_; }
  bool all_day_event() const { return all_day_event_; }
  const GURL& calendar_url() const { return calendar_url_; }
  const GURL& conference_url() const { return conference_url_; }
  const std::string& event_id() const { return event_id_; }
  const ResponseStatus& response_status() const { return response_status_; }

 private:
  static std::u16string GetSubtitle(base::Time start_time,
                                    base::Time end_time,
                                    bool all_day_event);

  // Returns a string like "10:00 AM - 10:30 AM".
  static std::u16string GetStartEndString(base::Time start_time,
                                          base::Time end_time);

  // Returns true if the "Join" button should be shown (i.e. the event has a
  // conference URL and the event is ongoing or happening soon).
  bool ShouldShowJoinButton() const;

  base::Time start_time_;
  base::Time end_time_;
  bool all_day_event_;
  // Link to the event in the Google Calendar UI.
  GURL calendar_url_;
  // Video conferencing URL (e.g. Google Meet).
  GURL conference_url_;
  std::string event_id_;
  // The user's current response to this calendar event.
  ResponseStatus response_status_;
};

// An attachment (e.g. a file attached to a calendar event). Represented as a
// separate BirchItem from the calendar event because the UI shows attachments
// separately (and ranks them independently).
class ASH_EXPORT BirchAttachmentItem : public BirchItem {
 public:
  BirchAttachmentItem(const std::u16string& title,
                      const GURL& file_url,
                      const GURL& icon_url,
                      const base::Time& start_time,
                      const base::Time& end_time,
                      const std::string& file_id);
  BirchAttachmentItem(BirchAttachmentItem&&);
  BirchAttachmentItem& operator=(BirchAttachmentItem&&);
  BirchAttachmentItem(const BirchAttachmentItem&);
  BirchAttachmentItem& operator=(const BirchAttachmentItem&);
  ~BirchAttachmentItem() override;

  // BirchItem:
  BirchItemType GetType() const override;
  std::string ToString() const override;
  void PerformAction() override;
  void LoadIcon(LoadIconCallback callback) const override;

  const GURL& file_url() const { return file_url_; }
  const GURL& icon_url() const { return icon_url_; }
  const base::Time& start_time() const { return start_time_; }
  const base::Time& end_time() const { return end_time_; }
  const std::string& file_id() const { return file_id_; }

 private:
  static std::u16string GetSubtitle(base::Time start_time, base::Time end_time);

  GURL file_url_;          // Link to the file.
  GURL icon_url_;          // Link to the file's icon's art asset.
  base::Time start_time_;  // Start time of the event (used for ranking).
  base::Time end_time_;    // End time of the event (used for ranking).
  std::string file_id_;    // ID of the file.
};

// A birch item which contains file path and time information.
class ASH_EXPORT BirchFileItem : public BirchItem {
 public:
  BirchFileItem(const base::FilePath& file_path,
                const std::optional<std::string>& title,
                const std::u16string& justification,
                base::Time timestamp,
                const std::string& file_id,
                const std::string& icon_url);
  BirchFileItem(BirchFileItem&&);
  BirchFileItem(const BirchFileItem&);
  BirchFileItem& operator=(const BirchFileItem&);
  bool operator==(const BirchFileItem& rhs) const;
  ~BirchFileItem() override;

  // BirchItem:
  BirchItemType GetType() const override;
  std::string ToString() const override;
  void PerformAction() override;
  void LoadIcon(LoadIconCallback callback) const override;

  const base::Time& timestamp() const { return timestamp_; }
  const std::string& file_id() const { return file_id_; }
  const std::string& icon_url() const { return icon_url_; }
  const base::FilePath& file_path() const { return file_path_; }

 private:
  static std::u16string GetTitle(const base::FilePath& file_path,
                                 const std::optional<std::string>& title);

  // A unique file id which is used to identify file type items, specifically
  // BirchFileItem and BirchAttachmentItem.
  std::string file_id_;
  std::string icon_url_;
  // For Google Drive documents the path looks like:
  // /media/fuse/drivefs-48de6bc248c2f6d8e809521347ef6190/root/Test doc.gdoc
  base::FilePath file_path_;
  base::Time timestamp_;
};

// A birch item which contains tab and session information.
class ASH_EXPORT BirchTabItem : public BirchItem {
 public:
  enum class DeviceFormFactor { kDesktop, kPhone, kTablet };

  BirchTabItem(const std::u16string& title,
               const GURL& url,
               const base::Time& timestamp,
               const GURL& favicon_url,
               const std::string& session_name,
               const DeviceFormFactor& form_factor);
  BirchTabItem(BirchTabItem&&);
  BirchTabItem(const BirchTabItem&);
  BirchTabItem& operator=(const BirchTabItem&);
  bool operator==(const BirchTabItem& rhs) const;
  ~BirchTabItem() override;

  // BirchItem:
  BirchItemType GetType() const override;
  std::string ToString() const override;
  void PerformAction() override;
  void LoadIcon(LoadIconCallback callback) const override;

  const GURL& url() const { return url_; }
  const base::Time& timestamp() const { return timestamp_; }
  const std::string& session_name() const { return session_name_; }
  DeviceFormFactor form_factor() const { return form_factor_; }
  const SecondaryIconType& secondary_icon_type() const {
    return secondary_icon_type_;
  }

 private:
  static std::u16string GetSubtitle(const std::string& session_name,
                                    base::Time timestamp);

  GURL url_;
  base::Time timestamp_;
  GURL favicon_url_;
  std::string session_name_;
  DeviceFormFactor form_factor_;
  SecondaryIconType secondary_icon_type_;
};

// A birch item for the last active URL.
class ASH_EXPORT BirchLastActiveItem : public BirchItem {
 public:
  BirchLastActiveItem(const std::u16string& title,
                      const GURL& page_url,
                      base::Time last_visit);
  BirchLastActiveItem(BirchLastActiveItem&&);
  BirchLastActiveItem(const BirchLastActiveItem&);
  BirchLastActiveItem& operator=(const BirchLastActiveItem&);
  bool operator==(const BirchLastActiveItem& rhs) const;
  ~BirchLastActiveItem() override;

  // BirchItem:
  BirchItemType GetType() const override;
  std::string ToString() const override;
  void PerformAction() override;
  void LoadIcon(LoadIconCallback callback) const override;

  const GURL& page_url() const { return page_url_; }

 private:
  static std::u16string GetSubtitle(base::Time last_visit);

  GURL page_url_;
};

// A birch item for a most-frequently-visited URL.
class ASH_EXPORT BirchMostVisitedItem : public BirchItem {
 public:
  BirchMostVisitedItem(const std::u16string& title, const GURL& page_url);
  BirchMostVisitedItem(BirchMostVisitedItem&&);
  BirchMostVisitedItem(const BirchMostVisitedItem&);
  BirchMostVisitedItem& operator=(const BirchMostVisitedItem&);
  bool operator==(const BirchMostVisitedItem& rhs) const;
  ~BirchMostVisitedItem() override;

  // BirchItem:
  BirchItemType GetType() const override;
  std::string ToString() const override;
  void PerformAction() override;
  void LoadIcon(LoadIconCallback callback) const override;

  const GURL& page_url() const { return page_url_; }

 private:
  static std::u16string GetSubtitle();

  GURL page_url_;
};

// A birch item which contains tabs shared to self information.
class ASH_EXPORT BirchSelfShareItem : public BirchItem {
 public:
  BirchSelfShareItem(const std::u16string& guid,
                     const std::u16string& title,
                     const GURL& url,
                     const base::Time& shared_time,
                     const std::u16string& device_name,
                     const SecondaryIconType& secondary_icon_type,
                     base::RepeatingClosure activation_callback);
  BirchSelfShareItem(BirchSelfShareItem&&);
  BirchSelfShareItem(const BirchSelfShareItem&);
  BirchSelfShareItem& operator=(const BirchSelfShareItem&);
  bool operator==(const BirchSelfShareItem& rhs) const;
  ~BirchSelfShareItem() override;

  // BirchItem:
  BirchItemType GetType() const override;
  std::string ToString() const override;
  void PerformAction() override;
  void LoadIcon(LoadIconCallback callback) const override;

  const std::u16string& guid() const { return guid_; }
  const base::Time& shared_time() const { return shared_time_; }
  const GURL& url() const { return url_; }
  const SecondaryIconType& secondary_icon_type() const {
    return secondary_icon_type_;
  }

 private:
  static std::u16string GetSubtitle(const std::u16string& device_name,
                                    base::Time shared_time);

  std::u16string guid_;
  GURL url_;
  base::Time shared_time_;
  SecondaryIconType secondary_icon_type_;
  // `activation_callback_` is triggered when the item is clicked by the user,
  // calling `OnItemPressed()` in `BirchSelfShareProvider` to mark the
  // corresponding `SendTabToSelfEntry` as opened.
  base::RepeatingClosure activation_callback_;
};

// A birch item which contains information about a tab that is currently playing
// media.
class ASH_EXPORT BirchLostMediaItem : public BirchItem {
 public:
  BirchLostMediaItem(const GURL& source_url,
                     const std::u16string& media_title,
                     const std::optional<ui::ImageModel>& backup_icon,
                     const SecondaryIconType& secondary_icon_type,
                     base::RepeatingClosure activation_callback);
  BirchLostMediaItem(BirchLostMediaItem&&);
  BirchLostMediaItem(const BirchLostMediaItem&);
  BirchLostMediaItem& operator=(const BirchLostMediaItem&);
  bool operator==(const BirchLostMediaItem& rhs) const;
  ~BirchLostMediaItem() override;

  // BirchItem:
  BirchItemType GetType() const override;
  std::string ToString() const override;
  void PerformAction() override;
  void LoadIcon(LoadIconCallback callback) const override;

  const GURL& source_url() const { return source_url_; }
  const std::u16string& media_title() const { return media_title_; }
  const SecondaryIconType& secondary_icon_type() const {
    return secondary_icon_type_;
  }

 private:
  static std::u16string GetSubtitle(SecondaryIconType type);

  GURL source_url_;
  std::u16string media_title_;
  std::optional<ui::ImageModel> backup_icon_;
  SecondaryIconType secondary_icon_type_;
  base::RepeatingClosure activation_callback_;
};

class ASH_EXPORT BirchWeatherItem : public BirchItem {
 public:
  BirchWeatherItem(const std::u16string& weather_description,
                   float temp_f,
                   const GURL& icon_url);
  BirchWeatherItem(BirchWeatherItem&&);
  BirchWeatherItem(const BirchWeatherItem&);
  BirchWeatherItem& operator=(const BirchWeatherItem&);
  bool operator==(const BirchWeatherItem& rhs) const;
  ~BirchWeatherItem() override;

  // BirchItem:
  BirchItemType GetType() const override;
  std::string ToString() const override;
  void PerformAction() override;
  void LoadIcon(LoadIconCallback callback) const override;
  std::u16string GetAccessibleName() const override;
  void PerformAddonAction() override;
  BirchAddonType GetAddonType() const override;

  float temp_f() const { return temp_f_; }

 private:
  static int GetTemperature(float temp_f);
  static bool UseCelsius();

  float temp_f_;
  GURL icon_url_;
};

class ASH_EXPORT BirchCoralItem : public BirchItem {
 public:
  BirchCoralItem(const std::u16string& coral_title,
                 const std::u16string& coral_text);
  BirchCoralItem(BirchCoralItem&&);
  BirchCoralItem(const BirchCoralItem&);
  BirchCoralItem& operator=(const BirchCoralItem&);
  bool operator==(const BirchCoralItem& rhs) const;
  ~BirchCoralItem() override;

  // BirchItem:
  BirchItemType GetType() const override;
  std::string ToString() const override;
  void PerformAction() override;
  void LoadIcon(LoadIconCallback callback) const override;
  void PerformAddonAction() override;
  BirchAddonType GetAddonType() const override;
  std::u16string GetAddonAccessibleName() const override;

 private:
  // TODO(yulunwu):Add coral data to `BirchCoralItem`
};

class ASH_EXPORT BirchReleaseNotesItem : public BirchItem {
 public:
  BirchReleaseNotesItem(const std::u16string& release_notes_title,
                        const std::u16string& release_notes_text,
                        const GURL& url,
                        base::Time first_seen);
  BirchReleaseNotesItem(BirchReleaseNotesItem&&) = default;
  BirchReleaseNotesItem(const BirchReleaseNotesItem&) = default;
  BirchReleaseNotesItem& operator=(const BirchReleaseNotesItem&) = default;
  bool operator==(const BirchReleaseNotesItem& rhs) const = default;
  ~BirchReleaseNotesItem() override;

  // BirchItem:
  BirchItemType GetType() const override;
  std::string ToString() const override;
  void PerformAction() override;
  void LoadIcon(LoadIconCallback callback) const override;

  const base::Time& first_seen() const { return first_seen_; }
  const GURL& url() const { return url_; }

 private:
  // The text to display in the suggestions.
  std::u16string release_notes_text_;

  // The URL that gets launched when the user clicks on the release notes birch
  // item.
  GURL url_;

  // The timestamp when the user first sees this item.
  base::Time first_seen_;
};

}  // namespace ash

#endif  // ASH_BIRCH_BIRCH_ITEM_H_