// 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_ASH_REMOTE_APPS_REMOTE_APPS_MANAGER_H_
#define CHROME_BROWSER_ASH_REMOTE_APPS_REMOTE_APPS_MANAGER_H_
#include <map>
#include <optional>
#include <string>
#include <vector>
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/scoped_observation.h"
#include "chrome/browser/apps/app_service/publishers/remote_apps.h"
#include "chrome/browser/ash/app_list/app_list_model_updater_observer.h"
#include "chrome/browser/ash/app_list/app_list_syncable_service.h"
#include "chrome/browser/ash/app_list/chrome_app_list_model_updater.h"
#include "chrome/browser/ash/remote_apps/remote_apps_impl.h"
#include "chrome/browser/ash/remote_apps/remote_apps_model.h"
#include "chrome/browser/ash/remote_apps/remote_apps_types.h"
#include "chromeos/components/remote_apps/mojom/remote_apps.mojom.h"
#include "components/keyed_service/core/keyed_service.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
class AppListModelUpdater;
class ChromeAppListItem;
class Profile;
namespace apps {
struct MenuItems;
} // namespace apps
namespace gfx {
class ImageSkia;
} // namespace gfx
namespace extensions {
class EventRouter;
} // namespace extensions
namespace ash {
class RemoteAppsImpl;
// KeyedService which manages the logic for |AppType::kRemote| in AppService.
// This service is created for Managed Guest Sessions and Regular User Sessions.
// The IDs of the added apps and folders are GUIDs generated using
// |base::Uuid::GenerateRandomV4().AsLowercaseString()|.
// See crbug.com/1101208 for more details on Remote Apps.
class RemoteAppsManager
: public KeyedService,
public apps::RemoteApps::Delegate,
public app_list::AppListSyncableService::Observer,
public AppListModelUpdaterObserver,
public chromeos::remote_apps::mojom::RemoteAppsFactory,
public chromeos::remote_apps::mojom::RemoteAppsLacrosBridge {
public:
class Observer : public base::CheckedObserver {
public:
~Observer() override = default;
// Invoked when an app is launched. |id| is the ID of the app.
virtual void OnAppLaunched(const std::string& id) {}
};
class ImageDownloader {
public:
virtual ~ImageDownloader() = default;
using DownloadCallback = base::OnceCallback<void(const gfx::ImageSkia&)>;
virtual void Download(const GURL& url, DownloadCallback callback) = 0;
};
explicit RemoteAppsManager(Profile* profile);
RemoteAppsManager(const RemoteAppsManager&) = delete;
RemoteAppsManager& operator=(const RemoteAppsManager&) = delete;
~RemoteAppsManager() override;
bool is_initialized() const { return is_initialized_; }
void BindFactoryInterface(
mojo::PendingReceiver<chromeos::remote_apps::mojom::RemoteAppsFactory>
pending_remote_apps_factory);
void BindLacrosBridgeInterface(
mojo::PendingReceiver<
chromeos::remote_apps::mojom::RemoteAppsLacrosBridge>
pending_remote_apps_lacros_bridge);
using AddAppCallback =
base::OnceCallback<void(const std::string& id, RemoteAppsError error)>;
// Adds a app with the given `name`. If `folder_id` is non-empty, the app is
// added to the folder with the given ID. The icon of the app is an image
// retrieved from `icon_url` and is retrieved asynchronously. If the icon has
// not been downloaded, or there is an error in downloading the icon, a
// placeholder icon will be used. If `add_to_front` is true and the app has
// no parent folder, the app will be added to the front of the app item list.
// `source_id` is a string used to identify the caller of this method. This
// identifier is typically an extension or app ID.
// The callback will be run with the ID of the added app, or an error if
// there is one.
// Adding to a non-existent folder will result in an error.
// Adding an app before the manager is initialized will result in an error.
void AddApp(const std::string& source_id,
const std::string& name,
const std::string& folder_id,
const GURL& icon_url,
bool add_to_front,
AddAppCallback callback);
// Adds a folder if the specified folder is missing in `model_updater_`.
void MaybeAddFolder(const std::string& folder_id);
// Returns a const pointer to the info of the specified app. If the app does
// not exist, returns a nullptr.
const RemoteAppsModel::AppInfo* GetAppInfo(const std::string& app_id) const;
// Deletes the app with id |id|.
// Deleting a non-existent app will result in an error.
RemoteAppsError DeleteApp(const std::string& id);
// Sorts the launcher items with the custom kAlphabeticalEphemeralAppFirst
// sort order which moves the remote apps to the front of the launcher.
void SortLauncherWithRemoteAppsFirst();
// Sets the list of apps to be pinned on the shelf. If `app_ids` are empty
// it should unpin all currently pinned apps.
RemoteAppsError SetPinnedApps(const std::vector<std::string>& app_ids);
// Adds a folder with |folder_name|. Note that empty folders are not shown in
// the launcher. Returns the ID for the added folder. If |add_to_front| is
// true, the folder will be added to the front of the app item list.
std::string AddFolder(const std::string& folder_name, bool add_to_front);
// Deletes the folder with id |folder_id|. All items in the folder are moved
// to the top-level in the launcher.
// Deleting a non-existent folder will result in an error.
RemoteAppsError DeleteFolder(const std::string& folder_id);
// Returns true if the app or folder with |id| should be added to the front
// of the app item list.
bool ShouldAddToFront(const std::string& id) const;
// KeyedService:
void Shutdown() override;
// chromeos::remote_apps::mojom::RemoteAppsFactory:
void BindRemoteAppsAndAppLaunchObserver(
const std::string& source_id,
mojo::PendingReceiver<chromeos::remote_apps::mojom::RemoteApps>
pending_remote_apps,
mojo::PendingRemote<chromeos::remote_apps::mojom::RemoteAppLaunchObserver>
pending_observer) override;
// chromeos::remote_apps::mojom::RemoteAppsLacrosBridge:
void BindRemoteAppsAndAppLaunchObserverForLacros(
mojo::PendingReceiver<chromeos::remote_apps::mojom::RemoteApps>
pending_remote_apps,
mojo::PendingRemote<chromeos::remote_apps::mojom::RemoteAppLaunchObserver>
pending_observer) override;
// apps::RemoteApps::Delegate:
const std::map<std::string, RemoteAppsModel::AppInfo>& GetApps() override;
void LaunchApp(const std::string& app_id) override;
gfx::ImageSkia GetIcon(const std::string& id) override;
gfx::ImageSkia GetPlaceholderIcon(const std::string& id,
int32_t size_hint_in_dip) override;
apps::MenuItems GetMenuModel(const std::string& id) override;
// app_list::AppListSyncableService::Observer:
void OnSyncModelUpdated() override;
// AppListModelUpdaterObserver:
void OnAppListItemAdded(ChromeAppListItem* item) override;
void SetImageDownloaderForTesting(
std::unique_ptr<ImageDownloader> image_downloader);
RemoteAppsModel* GetModelForTesting();
RemoteAppsImpl& GetRemoteAppsImpl() { return remote_apps_impl_; }
void SetIsInitializedForTesting(bool is_initialized);
private:
void Initialize();
void HandleOnAppAdded(const std::string& id);
void HandleOnFolderCreated(const std::string& folder_id);
void StartIconDownload(const std::string& id, const GURL& icon_url);
void OnIconDownloaded(const std::string& id, const gfx::ImageSkia& icon);
raw_ptr<Profile> profile_ = nullptr;
bool is_initialized_ = false;
raw_ptr<app_list::AppListSyncableService> app_list_syncable_service_ =
nullptr;
raw_ptr<AppListModelUpdater> model_updater_ = nullptr;
raw_ptr<extensions::EventRouter> event_router_ = nullptr;
std::unique_ptr<apps::RemoteApps> remote_apps_;
RemoteAppsImpl remote_apps_impl_{this};
std::unique_ptr<RemoteAppsModel> model_;
std::unique_ptr<ImageDownloader> image_downloader_;
base::ObserverList<Observer> observer_list_;
mojo::ReceiverSet<chromeos::remote_apps::mojom::RemoteAppsFactory>
factory_receivers_;
mojo::ReceiverSet<chromeos::remote_apps::mojom::RemoteAppsLacrosBridge>
bridge_receivers_;
// Map from id to callback. The callback is run after |OnAppUpdate| for the
// app has been observed.
std::map<std::string, AddAppCallback> add_app_callback_map_;
std::map<std::string, std::string> app_id_to_source_id_map_;
base::ScopedObservation<app_list::AppListSyncableService,
app_list::AppListSyncableService::Observer>
app_list_syncable_service_observation_{this};
base::ScopedObservation<AppListModelUpdater, AppListModelUpdaterObserver>
app_list_model_updater_observation_{this};
base::WeakPtrFactory<RemoteAppsManager> weak_factory_{this};
};
} // namespace ash
#endif // CHROME_BROWSER_ASH_REMOTE_APPS_REMOTE_APPS_MANAGER_H_