chromium/components/arc/common/intent_helper/activity_icon_loader.h

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

#ifndef COMPONENTS_ARC_COMMON_INTENT_HELPER_ACTIVITY_ICON_LOADER_H_
#define COMPONENTS_ARC_COMMON_INTENT_HELPER_ACTIVITY_ICON_LOADER_H_

#include <map>
#include <memory>
#include <string>
#include <vector>

#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "build/chromeos_buildflags.h"
#include "ui/base/resource/resource_scale_factor.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
#include "url/gurl.h"

#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/components/arc/mojom/intent_helper.mojom.h"
#elif BUILDFLAG(IS_CHROMEOS_LACROS)
#include "chromeos/crosapi/mojom/arc.mojom.h"  // nogncheck
#else
#error "ARC files should only be included for Ash-chrome or Lacros-chrome."
#endif

namespace arc {

class AdaptiveIconDelegate;

namespace internal {

// A class which retrieves an activity icon from ARC.
class ActivityIconLoader {
 public:
  struct Icons {
    Icons(const gfx::Image& icon16,
          const gfx::Image& icon20,
          const scoped_refptr<base::RefCountedData<GURL>>& icon16_dataurl);
    Icons(const Icons& other);
    ~Icons();

    const gfx::Image icon16;                                         // 16 dip
    const gfx::Image icon20;                                         // 20 dip
    const scoped_refptr<base::RefCountedData<GURL>> icon16_dataurl;  // as URL
  };

  struct ActivityName {
    ActivityName(const std::string& package_name,
                 const std::string& activity_name);
    ~ActivityName();
    bool operator<(const ActivityName& other) const;

    std::string package_name;
    // Can be empty. When |activity_name| is empty, the loader tries to fetch
    // the package's default icon.
    std::string activity_name;
  };

  enum class GetResult {
    // Succeeded. The callback will be called asynchronously.
    SUCCEEDED_ASYNC,
    // Succeeded. The callback has already been called synchronously.
    SUCCEEDED_SYNC,
    // Failed. The intent_helper instance is not yet ready. This is a temporary
    // error.
    FAILED_ARC_NOT_READY,
    // Failed. Either ARC is not supported at all or intent_helper instance
    // version is too old.
    FAILED_ARC_NOT_SUPPORTED,
  };

  // Ash uses arc::mojom interface while Lacros uses crosapi::mojom.
#if BUILDFLAG(IS_CHROMEOS_ASH)
  using ActivityIconPtr = mojom::ActivityIconPtr;
  using ActivityNamePtr = mojom::ActivityNamePtr;
  using ScaleFactor = mojom::ScaleFactor;
#else  // BUILDFLAG(IS_CHROMEOS_LACROS)
  using ActivityIconPtr = crosapi::mojom::ActivityIconPtr;
  using ActivityNamePtr = crosapi::mojom::ActivityNamePtr;
  using ScaleFactor = crosapi::mojom::ScaleFactor;
#endif

  using ActivityToIconsMap = std::map<ActivityName, Icons>;
  using OnIconsReadyCallback =
      base::OnceCallback<void(std::unique_ptr<ActivityToIconsMap>)>;

  ActivityIconLoader();
  ActivityIconLoader(const ActivityIconLoader&) = delete;
  ActivityIconLoader& operator=(const ActivityIconLoader&) = delete;
  ~ActivityIconLoader();

  void SetAdaptiveIconDelegate(AdaptiveIconDelegate* delegate);

  // Removes icons associated with |package_name| from the cache.
  void InvalidateIcons(const std::string& package_name);

  // Retrieves icons for the |activities| and calls |cb|. The |cb| is called
  // back exactly once, either synchronously in the GetActivityIcons() when
  // the result is _not_ SUCCEEDED_ASYNC (i.e. all icons are already cached
  // locally or ARC is not ready/supported). Otherwise, the callback is run
  // later asynchronously with icons fetched from ARC side.
  GetResult GetActivityIcons(const std::vector<ActivityName>& activities,
                             OnIconsReadyCallback cb);

  void OnIconsResizedForTesting(OnIconsReadyCallback cb,
                                std::unique_ptr<ActivityToIconsMap> result);
  void AddCacheEntryForTesting(const ActivityName& activity);

  void OnIconsReadyForTesting(std::unique_ptr<ActivityToIconsMap> cached_result,
                              OnIconsReadyCallback cb,
                              std::vector<ActivityIconPtr> icons);

  // Returns true if |result| indicates that the |cb| object passed to
  // GetActivityIcons() has already called.
  static bool HasIconsReadyCallbackRun(GetResult result);

  const ActivityToIconsMap& cached_icons_for_testing() { return cached_icons_; }

 private:
  // A function called when the mojo IPC returns.
  void OnIconsReady(std::unique_ptr<ActivityToIconsMap> cached_result,
                    OnIconsReadyCallback cb,
                    std::vector<ActivityIconPtr> icons);

  // A function called when the adaptive icons are generated.
  void OnAdaptiveIconGenerated(
      std::vector<ActivityName> actvity_names,
      std::unique_ptr<ActivityToIconsMap> cached_result,
      OnIconsReadyCallback cb,
      const std::vector<gfx::ImageSkia>& adaptive_icons);

  // A function called when ResizeIcons finishes. Append items in |result| to
  // |cached_icons_|.
  void OnIconsResized(std::unique_ptr<ActivityToIconsMap> cached_result,
                      OnIconsReadyCallback cb,
                      std::unique_ptr<ActivityToIconsMap> result);

  // The maximum scale factor the current platform supports.
  const ui::ResourceScaleFactor scale_factor_;
  // A map which holds icons in a scale-factor independent form (gfx::Image).
  ActivityToIconsMap cached_icons_;

  // A delegate which converts the icon to the adaptive icon.
  raw_ptr<AdaptiveIconDelegate> delegate_ = nullptr;

  THREAD_CHECKER(thread_checker_);

  // This must come last to make sure weak pointers are invalidated first.
  base::WeakPtrFactory<ActivityIconLoader> weak_ptr_factory_{this};
};

}  // namespace internal
}  // namespace arc

#endif  // COMPONENTS_ARC_COMMON_INTENT_HELPER_ACTIVITY_ICON_LOADER_H_