chromium/chrome/browser/sharesheet/sharesheet_service.h

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

#ifndef CHROME_BROWSER_SHARESHEET_SHARESHEET_SERVICE_H_
#define CHROME_BROWSER_SHARESHEET_SHARESHEET_SERVICE_H_

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

#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/apps/app_service/app_service_proxy_forward.h"
#include "chrome/browser/sharesheet/share_action/share_action_cache.h"
#include "chrome/browser/sharesheet/sharesheet_controller.h"
#include "chrome/browser/sharesheet/sharesheet_metrics.h"
#include "chrome/browser/sharesheet/sharesheet_types.h"
#include "chromeos/components/sharesheet/constants.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/services/app_service/public/cpp/icon_types.h"
#include "components/services/app_service/public/cpp/intent.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/gfx/native_widget_types.h"

class Profile;

namespace apps {
struct IntentLaunchInfo;
}  // namespace apps

namespace views {
class View;
}

namespace content {
class WebContents;
}

namespace gfx {
struct VectorIcon;
}

namespace sharesheet {

class SharesheetServiceDelegator;
class SharesheetUiDelegate;

// The SharesheetService is the root service that provides a sharesheet for
// Chrome desktop.
class SharesheetService : public KeyedService {
 public:
  using GetNativeWindowCallback = base::OnceCallback<gfx::NativeWindow()>;

  explicit SharesheetService(Profile* profile);
  ~SharesheetService() override;

  SharesheetService(const SharesheetService&) = delete;
  SharesheetService& operator=(const SharesheetService&) = delete;

  // Displays the dialog (aka bubble) for sharing content (or files) with
  // other applications and targets. `intent` contains the list of the
  // files/content to be shared. If the files to share contains Google
  // Drive hosted document, only drive share action will be shown.
  //
  // `delivered_callback` is run to signify that the intent has been
  // delivered to the target selected by the user (which may then show its own
  // separate UI, e.g. for Nearby Sharing). `delivered_callback` must be
  // non-null.
  // `close_callback` is run to signify that the share flow has finished and the
  // dialog has closed (this includes separate UI, e.g. Nearby Sharing).
  void ShowBubble(content::WebContents* web_contents,
                  apps::IntentPtr intent,
                  LaunchSource source,
                  DeliveredCallback delivered_callback,
                  CloseCallback close_callback = base::NullCallback());
  void ShowBubble(apps::IntentPtr intent,
                  LaunchSource source,
                  GetNativeWindowCallback get_native_window_callback,
                  DeliveredCallback delivered_callback,
                  CloseCallback close_callback = base::NullCallback());

  // Gets the sharesheet controller for the given |native_window|.
  SharesheetController* GetSharesheetController(
      gfx::NativeWindow native_window);
#if BUILDFLAG(IS_CHROMEOS_ASH)
  // Skips the generic Sharesheet bubble and directly displays the
  // NearbyShare bubble dialog for ARC.
  void ShowNearbyShareBubbleForArc(gfx::NativeWindow native_window,
                                   apps::IntentPtr intent,
                                   LaunchSource source,
                                   DeliveredCallback delivered_callback,
                                   CloseCallback close_callback,
                                   ActionCleanupCallback cleanup_callback);
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
  // |share_action_type| is set to null when testing, but should otherwise have
  // a valid value.
  void OnBubbleClosed(gfx::NativeWindow native_window,
                      const std::optional<ShareActionType>& share_action_type);

  // OnTargetSelected is called by both apps and share actions.
  // If |type| is kAction, expect |share_action_type| to have a valid
  // ShareActionType. If |type| is kArcApp or kWebApp, expect |app_name|
  // to contain a valid app name.
  void OnTargetSelected(gfx::NativeWindow native_window,
                        const TargetType type,
                        const std::optional<ShareActionType>& share_action_type,
                        const std::optional<std::u16string>& app_name,
                        apps::IntentPtr intent,
                        views::View* share_action_view);

  // Only share actions, which have a |share_action_type|, call this function.
  bool OnAcceleratorPressed(const ui::Accelerator& accelerator,
                            const ShareActionType share_action_type);

  // If the files to share contains a Google Drive hosted document, only the
  // drive share action will be shown.
  bool HasShareTargets(const apps::IntentPtr& intent);

  Profile* GetProfile();

  // Only share actions, which have a |share_action_type|, are expected to have
  // a vector icon. Return nullptr if |share_action_type| is null.
  const gfx::VectorIcon* GetVectorIcon(
      const std::optional<ShareActionType>& share_action_type);

  // ==========================================================================
  // ========================== Testing APIs ==================================
  // ==========================================================================
  void ShowBubbleForTesting(gfx::NativeWindow native_window,
                            apps::IntentPtr intent,
                            LaunchSource source,
                            DeliveredCallback delivered_callback,
                            CloseCallback close_callback,
                            int num_actions_to_add);
  SharesheetUiDelegate* GetUiDelegateForTesting(
      gfx::NativeWindow native_window);
  static void SetSelectedAppForTesting(const std::u16string& target_name);

 private:
  using SharesheetServiceIconLoaderCallback =
      base::OnceCallback<void(std::vector<TargetInfo> targets)>;

  void PrepareToShowBubble(apps::IntentPtr intent,
                           GetNativeWindowCallback get_native_window_callback,
                           DeliveredCallback delivered_callback,
                           CloseCallback close_callback);

  std::vector<TargetInfo> GetActionsForIntent(const apps::IntentPtr& intent);

  void LoadAppIcons(std::vector<apps::IntentLaunchInfo> intent_launch_info,
                    std::vector<TargetInfo> targets,
                    size_t index,
                    SharesheetServiceIconLoaderCallback callback);

  void OnIconLoaded(std::vector<apps::IntentLaunchInfo> intent_launch_info,
                    std::vector<TargetInfo> targets,
                    size_t index,
                    SharesheetServiceIconLoaderCallback callback,
                    apps::IconValuePtr icon_value);

  void OnAppIconsLoaded(apps::IntentPtr intent,
                        GetNativeWindowCallback get_native_window_callback,
                        DeliveredCallback delivered_callback,
                        CloseCallback close_callback,
                        std::vector<TargetInfo> targets);

  void OnReadyToShowBubble(gfx::NativeWindow native_window,
                           apps::IntentPtr intent,
                           DeliveredCallback delivered_callback,
                           CloseCallback close_callback,
                           std::vector<TargetInfo> targets);

  void LaunchApp(const std::u16string& target_name, apps::IntentPtr intent);

  SharesheetServiceDelegator* GetOrCreateDelegator(
      gfx::NativeWindow native_window);
  SharesheetServiceDelegator* GetDelegator(gfx::NativeWindow native_window);

  void RecordUserActionMetrics(
      const std::optional<ShareActionType>& share_action_type,
      const std::optional<std::u16string>& app_name);
  void RecordTargetCountMetrics(const std::vector<TargetInfo>& targets);
  // Makes |intent| related UMA recordings.
  void RecordShareDataMetrics(const apps::IntentPtr& intent);

  raw_ptr<Profile> profile_;
  std::unique_ptr<ShareActionCache> share_action_cache_;
  raw_ptr<apps::AppServiceProxy> app_service_proxy_;

  // Record of all active SharesheetServiceDelegators. These can be retrieved
  // by ShareActions and used as SharesheetControllers to make bubble changes.
  std::vector<std::unique_ptr<SharesheetServiceDelegator>> active_delegators_;

  base::WeakPtrFactory<SharesheetService> weak_factory_{this};
};

}  // namespace sharesheet

#endif  // CHROME_BROWSER_SHARESHEET_SHARESHEET_SERVICE_H_