chromium/chrome/browser/ui/ash/projector/pending_screencast_manager.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 CHROME_BROWSER_UI_ASH_PROJECTOR_PENDING_SCREENCAST_MANAGER_H_
#define CHROME_BROWSER_UI_ASH_PROJECTOR_PENDING_SCREENCAST_MANAGER_H_

#include <map>
#include <memory>

#include "ash/webui/projector_app/projector_app_client.h"
#include "ash/webui/projector_app/projector_xhr_sender.h"
#include "base/functional/callback.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
#include "chrome/browser/ash/extensions/file_manager/scoped_suppress_drive_notifications_for_path.h"
#include "chrome/browser/ui/ash/projector/projector_drivefs_provider.h"
#include "chromeos/ash/components/drivefs/drivefs_host.h"

namespace drivefs {
namespace mojom {
class SyncingStatus;
class DriveError;
}  // namespace mojom
}  // namespace drivefs

namespace base {
class FilePath;
}

// A callback to notify the change of pending screencasts to
// ProjectorAppClient::Observer. The argument is the set of pending screencasts
// owned by PendingScreencastManager.
using PendingScreencastChangeCallback =
    base::RepeatingCallback<void(const ash::PendingScreencastContainerSet&)>;

// A class that handles pending screencast events.
class PendingScreencastManager : drivefs::DriveFsHost::Observer {
 public:
  explicit PendingScreencastManager(
      PendingScreencastChangeCallback pending_screencast_change_callback);
  PendingScreencastManager(const PendingScreencastManager&) = delete;
  PendingScreencastManager& operator=(const PendingScreencastManager&) = delete;
  ~PendingScreencastManager() override;

  // DriveFsHost::Observer implementation.
  using drivefs::DriveFsHost::Observer::GetHost;
  void OnUnmounted() override;
  void OnSyncingStatusUpdate(
      const drivefs::mojom::SyncingStatus& status) override;
  void OnError(const drivefs::mojom::DriveError& error) override;

  // Returns a list of pending screencast from `pending_screencast_cache_`.
  const ash::PendingScreencastContainerSet& GetPendingScreencasts() const;

  // Maybe observe the current active profile.
  void MaybeSwitchDriveFsObservation();

  // Adds `screencast_paths` to `paths_notifications_suppressors_` and
  // suppresses notification for these paths if `suppress` is true. Removes
  // `screencast_paths` from `paths_notifications_suppressors_` when
  // `suppress` is false.
  void ToggleFileSyncingNotificationForPaths(
      const std::vector<base::FilePath>& screencast_paths,
      bool suppress);

  // Resets (`is_active` is false) or creates (`is_active` is true) values for
  // all keys stored in `paths_notifications_suppressors_`.
  void OnAppActiveStatusChanged(bool is_active);

  // Test only:
  base::TimeTicks last_pending_screencast_change_tick() const {
    return last_pending_screencast_change_tick_;
  }
  scoped_refptr<base::SequencedTaskRunner> blocking_task_runner() {
    return blocking_task_runner_;
  }

  using OnGetFileIdCallback =
      base::OnceCallback<void(const base::FilePath& local_file_path,
                              const std::string& file_id)>;
  void SetOnGetFileIdCallbackForTest(OnGetFileIdCallback callback);
  using OnGetRequestBodyCallback =
      base::OnceCallback<void(const std::string& file_id,
                              const std::string& request_body)>;
  void SetOnGetRequestBodyCallbackForTest(OnGetRequestBodyCallback callback);
  void SetProjectorXhrSenderForTest(
      std::unique_ptr<ash::ProjectorXhrSender> xhr_sender);

 private:
  // Updates `pending_screencast_cache_` and notifies pending screencast change.
  void OnProcessAndGenerateNewScreencastsFinished(
      const base::TimeTicks task_start_tick,
      const ash::PendingScreencastContainerSet& screencasts);

  // Called when the `event_file` is synced to Drive. Removed completedly synced
  // files from `error_syncing_files_` and `syncing_metadata_files_` cached. If
  // it is a screencast metadata file, post task to update indexable text.
  void OnFileSyncedCompletely(const base::FilePath& event_file);

  void OnGetFileId(const base::FilePath& local_file_path,
                   const std::string& file_id);

  // Sends a patch request to patch file metadata. `file_id` is the Drive server
  // side file id.
  void SendDrivePatchRequest(const std::string& file_id,
                             const std::string& request_body);

  // TODO(b/221902328): Fix the case that user might delete files through file
  // app.

  // A set that caches current pending screencast.
  ash::PendingScreencastContainerSet pending_screencast_cache_;

  // A set of files failed to upload to Drive.
  std::set<base::FilePath> error_syncing_files_;

  // A set of syncing screencast metadata files, which have ".projector"
  // extension. This set is used to track which metadata files are being
  // uploaded so we only update the indexable text once. File is removed from
  // the set after updating indexable text completed.
  std::set<base::FilePath> syncing_metadata_files_;

  // A callback to notify pending screencast status change.
  PendingScreencastChangeCallback pending_screencast_change_callback_;

  // A blocking task runner for file IO operations.
  scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;

  // The time tick when last `pending_screencast_change_callback_` was called.
  // Could be null if last `pending_screencast_change_callback_` was called with
  // empty screencasts set or no `pending_screencast_change_callback_` invoked
  // in the current ChromeOS session.
  base::TimeTicks last_pending_screencast_change_tick_;

  // Not available if user never uploads a screencast during current ChromeOS
  // session.
  std::unique_ptr<ash::ProjectorXhrSender> xhr_sender_;

  // Updates indexable text containing a lot of async steps. These callbacks are
  // used in tests to verify the task quit correctly while error happens.
  OnGetRequestBodyCallback on_get_request_body_;
  OnGetFileIdCallback on_get_file_id_callback_;

  ProjectorDriveFsProvider drive_helper_;

  // A map to store `file_manager::ScopedSuppressDriveNotificationsForPath`. The
  // entries get created/destroyed on calling
  // `ToggleFileSyncingNotificationForPaths`, or when files whose paths are
  // stored in this map are uploaded completely. All unique pointers get reset
  // on app UI destroyed and re-created on app UI active.
  std::map<
      base::FilePath,
      std::unique_ptr<file_manager::ScopedSuppressDriveNotificationsForPath>>
      paths_notifications_suppressors_;

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

#endif  // CHROME_BROWSER_UI_ASH_PROJECTOR_PENDING_SCREENCAST_MANAGER_H_