chromium/ash/ambient/model/ambient_animation_photo_provider.h

// Copyright 2021 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_AMBIENT_MODEL_AMBIENT_ANIMATION_PHOTO_PROVIDER_H_
#define ASH_AMBIENT_MODEL_AMBIENT_ANIMATION_PHOTO_PROVIDER_H_

#include <functional>
#include <string>
#include <string_view>
#include <vector>

#include "ash/ambient/model/ambient_backend_model.h"
#include "ash/ambient/util/ambient_util.h"
#include "ash/ash_export.h"
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "cc/paint/skottie_frame_data_provider.h"
#include "cc/paint/skottie_resource_metadata.h"

namespace ash {

class AmbientAnimationStaticResources;

// The |SkottieFrameDataProvider| implementation for ambient mode animations is
// tied to a single animation instance for its lifetime. It has 2 purposes:
// 1) Load "static" image assets (those that are fixed for the lifetime of the
//    animation) from storage. These are fixtures in the animation design such
//    as background images and are only loaded once in the animation's lifetime.
// 2) Pull images from the |AmbientBackendModel| for "dynamic" image assets,
//    which are the spots where the photos of interest go. Currently, a new set
//    of images are loaded from the model at the start of each new animation
//    cycle.
class ASH_EXPORT AmbientAnimationPhotoProvider
    : public cc::SkottieFrameDataProvider {
 public:
  class Observer : public base::CheckedObserver {
   public:
    // Invoked whenever the dynamic assets in the animation are assigned a new
    // set of photos (reflected in |new_topics|). There shall be one entry in
    // |new_topics| for each dynamic asset in the animation, where the entry's
    // value is the topic that the asset was just assigned.
    //
    // Note in the event that the same topic is assigned to multiple dynamic
    // assets, that topic will appear multiple times in |new_topics|.
    virtual void OnDynamicImageAssetsRefreshed(
        const base::flat_map<ambient::util::ParsedDynamicAssetId,
                             std::reference_wrapper<const PhotoWithDetails>>&
            new_topics) = 0;

   protected:
    ~Observer() override = default;
  };

  AmbientAnimationPhotoProvider(
      const AmbientAnimationStaticResources* static_resources,
      const AmbientBackendModel* backend_model);
  ~AmbientAnimationPhotoProvider() override;

  scoped_refptr<ImageAsset> LoadImageAsset(
      std::string_view resource_id,
      const base::FilePath& resource_path,
      const std::optional<gfx::Size>& size) override;

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

  // Sets whether the static image asset in the animation with the given
  // |asset_id| is enabled or not. If an image asset is disabled, the rest of
  // the animation can still render properly; the specified static image asset
  // will just be missing. By default, all static image assets are enabled
  // until specified otherwise by the caller.
  //
  // Returns true on success; false if |asset_id| is unknown.
  bool ToggleStaticImageAsset(cc::SkottieResourceIdHash asset_id, bool enabled);

 private:
  class DynamicImageAssetImpl;
  class StaticImageAssetImpl;

  struct OrderDynamicAssetsByIdx {
    bool operator()(const scoped_refptr<DynamicImageAssetImpl>& asset_l,
                    const scoped_refptr<DynamicImageAssetImpl>& asset_r) const;
  };

  using DynamicAssetSet = base::flat_set<scoped_refptr<DynamicImageAssetImpl>,
                                         OrderDynamicAssetsByIdx>;

  PhotoWithDetails GenerateNextTopicForDynamicAsset(
      const DynamicImageAssetImpl& asset);
  PhotoWithDetails ExtractPendingTopicForDynamicAsset(
      const DynamicImageAssetImpl& asset);
  void RotateDynamicAssetTopics();

  std::vector<std::reference_wrapper<const PhotoWithDetails>>
  GetTopicsToChooseFrom() const;

  void NotifyObserverOfNewTopics();
  void RecordDynamicAssetMetrics();

  // Whether the tree shadow asset should be set. See the comment in
  // `AmbientAnimationView::OnViewBoundsChanged()`.
  bool enable_tree_shadow_ = false;

  // Unowned pointers. Must outlive the |AmbientAnimationPhotoProvider|.
  const raw_ptr<const AmbientAnimationStaticResources> static_resources_;
  const raw_ptr<const AmbientBackendModel> backend_model_;

  // Map's key is hash of the static image asset's string id.
  base::flat_map<cc::SkottieResourceIdHash, scoped_refptr<StaticImageAssetImpl>>
      static_assets_;
  base::flat_map</*position_id*/ std::string, DynamicAssetSet>
      dynamic_assets_per_position_;
  size_t total_num_dynamic_assets_ = 0;
  base::flat_map<const DynamicImageAssetImpl*, PhotoWithDetails>
      pending_dynamic_asset_topics_;
  base::ObserverList<Observer> observers_;
  base::WeakPtrFactory<AmbientAnimationPhotoProvider> weak_factory_;
};

}  // namespace ash

#endif  // ASH_AMBIENT_MODEL_AMBIENT_ANIMATION_PHOTO_PROVIDER_H_