chromium/chrome/browser/nearby_sharing/nearby_notification_manager.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_NEARBY_SHARING_NEARBY_NOTIFICATION_MANAGER_H_
#define CHROME_BROWSER_NEARBY_SHARING_NEARBY_NOTIFICATION_MANAGER_H_

#include <optional>

#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "chrome/browser/nearby_sharing/nearby_notification_delegate.h"
#include "chrome/browser/nearby_sharing/nearby_sharing_service.h"
#include "chrome/browser/nearby_sharing/share_target.h"
#include "chrome/browser/nearby_sharing/share_target_discovered_callback.h"
#include "chrome/browser/nearby_sharing/transfer_metadata.h"
#include "chrome/browser/nearby_sharing/transfer_metadata_builder.h"
#include "chrome/browser/nearby_sharing/transfer_update_callback.h"

class NotificationDisplayService;
class PrefService;
class Profile;
class SkBitmap;

// Manages notifications shown for Nearby Share. Only a single notification will
// be shown as simultaneous connections are not supported. All methods should be
// called from the UI thread.
class NearbyNotificationManager : public TransferUpdateCallback,
                                  public ShareTargetDiscoveredCallback,
                                  public NearbySharingService::Observer {
 public:
  static constexpr base::TimeDelta kNearbyDeviceTryingToShareDismissedTimeout =
      base::Minutes(15);

  enum class SuccessNotificationAction {
    kNone,
    kCopyText,
    kCopyImage,
    kOpenDownloads,
    kOpenUrl,
    kOpenWifiNetworksList,
  };

  // Type of content we received that determines the actions we provide.
  enum class ReceivedContentType {
    kFiles,            // One or more generic files
    kSingleImage,      // One image that will be shown as a preview
    kSingleUrl,        // One URL that will be opened on click.
    kText,             // Arbitrary text content
    kWifiCredentials,  // Wi-Fi credentials for a network configuration
  };

  class SettingsOpener {
   public:
    SettingsOpener() = default;
    SettingsOpener(SettingsOpener&) = delete;
    SettingsOpener& operator=(SettingsOpener&) = delete;
    virtual ~SettingsOpener() = default;

    // Open the chromeos settings page at the given uri, using
    // |chrome::SettingsWindowManager| by default.
    virtual void ShowSettingsPage(Profile* profile,
                                  const std::string& sub_page);
  };

  NearbyNotificationManager(
      NotificationDisplayService* notification_display_service,
      NearbySharingService* nearby_service,
      PrefService* pref_service,
      Profile* profile);
  ~NearbyNotificationManager() override;

  // TransferUpdateCallback:
  void OnTransferUpdate(const ShareTarget& share_target,
                        const TransferMetadata& transfer_metadata) override;

  // ShareTargetDiscoveredCallback:
  void OnShareTargetDiscovered(ShareTarget share_target) override;
  void OnShareTargetLost(ShareTarget share_target) override;

  // NearbySharingService::Observer
  void OnHighVisibilityChanged(bool in_high_visibility) override {}
  void OnNearbyProcessStopped() override;
  void OnShutdown() override {}
  void OnFastInitiationDevicesDetected() override;
  void OnFastInitiationDevicesNotDetected() override;
  void OnFastInitiationScanningStopped() override;

  // Shows a progress notification of the data being transferred to or from
  // |share_target|. Has a cancel action to cancel the transfer.
  void ShowProgress(const ShareTarget& share_target,
                    const TransferMetadata& transfer_metadata);

  // Shows an incoming connection request notification from |share_target|
  // wanting to send data to this device. Has a decline action and optionally an
  // accept action if the transfer needs to be accepted on the local device.
  void ShowConnectionRequest(const ShareTarget& share_target,
                             const TransferMetadata& transfer_metadata);

  // Show the notification that a nearby device is trying to share. If the user
  // is not yet onboarded this notification will prompt the user to set up
  // Nearby Share and redirect to the onboarding flow. If the user has already
  // onboarded this notification will prompt the user to go into high visibility
  // mode.
  void ShowNearbyDeviceTryingToShare();

  // Shows a notification for send or receive success.
  void ShowSuccess(const ShareTarget& share_target);

  // Shows a notification for send or receive failure.
  void ShowFailure(const ShareTarget& share_target,
                   const TransferMetadata& transfer_metadata);

  // Shows a notification for send or receive cancellation.
  void ShowCancelled(const ShareTarget& share_target);

  // Shows a notification to remind users of their current visibility selection.
  void ShowVisibilityReminder();

  // Closes any currently shown transfer notification (e.g. progress or
  // connection).
  void CloseTransfer();

  // Closes any currently shown notification that a nearby device is trying to
  // share. It does not have any effect on the actual onboarding UI or the high
  // visibility mode UI.
  void CloseNearbyDeviceTryingToShare();

  // Closes any currently shown nearby visibility reminder notification.
  void CloseVisibilityReminder();

  // Gets the currently registered delegate for |notification_id|.
  NearbyNotificationDelegate* GetNotificationDelegate(
      const std::string& notification_id);

  void OpenURL(GURL url);

  // Opens Wi-Fi Networks subpage in Settings.
  void OpenWifiNetworksList();

  // Cancels the currently in progress transfer.
  void CancelTransfer();

  // Rejects the currently in progress transfer.
  void RejectTransfer();

  // Accepts the currently in progress transfer.
  void AcceptTransfer();

  // Called when the nearby device is trying notification got clicked.
  void OnNearbyDeviceTryingToShareClicked();

  // Called when the nearby device is trying notification got dismissed. We
  // won't show another one for a certain time period after this.
  void OnNearbyDeviceTryingToShareDismissed(bool did_click_dismiss);

  void CloseSuccessNotification(const std::string& notification_id);

  // Called when the nearby visibility reminder notification got clicked.
  void OnNearbyVisibilityReminderClicked();

  // Called when the nearby visibility reminder notification got dismissed.
  void OnNearbyVisibilityReminderDismissed();

  void SetOnSuccessClickedForTesting(
      base::OnceCallback<void(SuccessNotificationAction)> callback);
  void SetSettingsOpenerForTesting(
      std::unique_ptr<SettingsOpener> settings_opener);

 private:
  void ShowIncomingSuccess(const ShareTarget& share_target,
                           ReceivedContentType type,
                           const SkBitmap& image);

  raw_ptr<NotificationDisplayService, DanglingUntriaged>
      notification_display_service_;
  raw_ptr<NearbySharingService> nearby_service_;
  raw_ptr<PrefService> pref_service_;
  raw_ptr<Profile> profile_;
  std::unique_ptr<SettingsOpener> settings_opener_;

  // Maps notification ids to notification delegates.
  base::flat_map<std::string, std::unique_ptr<NearbyNotificationDelegate>>
      delegate_map_;

  // ShareTarget of the current transfer.
  std::optional<ShareTarget> share_target_;

  // Last transfer status reported to OnTransferUpdate(). Null when no transfer
  // is in progress.
  std::optional<TransferMetadata::Status> last_transfer_status_;

  // The last time that 'Nearby device is trying to share' notification was
  // shown.
  base::TimeTicks last_device_nearby_sharing_notification_shown_timestamp_;

  base::OnceCallback<void(SuccessNotificationAction)>
      success_action_test_callback_;

  base::WeakPtrFactory<NearbyNotificationManager> weak_ptr_factory_{this};
};

#endif  // CHROME_BROWSER_NEARBY_SHARING_NEARBY_NOTIFICATION_MANAGER_H_