// 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 CHROME_BROWSER_ASH_NOTE_TAKING_NOTE_TAKING_HELPER_H_
#define CHROME_BROWSER_ASH_NOTE_TAKING_NOTE_TAKING_HELPER_H_
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "ash/components/arc/mojom/intent_helper.mojom-forward.h"
#include "base/functional/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/scoped_multi_source_observation.h"
#include "base/scoped_observation.h"
#include "chrome/browser/ash/arc/session/arc_session_manager_observer.h"
#include "chrome/browser/ash/lock_screen_apps/lock_screen_apps.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_manager_observer.h"
#include "components/arc/intent_helper/arc_intent_helper_bridge.h"
#include "components/arc/intent_helper/arc_intent_helper_observer.h"
#include "components/services/app_service/public/cpp/app_registry_cache.h"
class Profile;
namespace apps {
class AppUpdate;
}
namespace content {
class BrowserContext;
} // namespace content
namespace extensions {
class Extension;
namespace api {
namespace app_runtime {
struct ActionData;
} // namespace app_runtime
} // namespace api
} // namespace extensions
namespace ash {
class NoteTakingControllerClient;
// Information about an installed note-taking app.
struct NoteTakingAppInfo {
// Application name to display to user.
std::string name;
// Either an extension ID (in the case of a Chrome app) or a package name (in
// the case of an Android app) or a web app ID (in the case of a web app).
std::string app_id;
// True if this is the preferred note-taking app.
bool preferred;
// Whether the app supports use on the Chrome OS lock screen.
LockScreenAppSupport lock_screen_support;
};
// Singleton class used to launch a note-taking app.
class NoteTakingHelper : public arc::ArcIntentHelperObserver,
public arc::ArcSessionManagerObserver,
public apps::AppRegistryCache::Observer,
public ProfileManagerObserver {
public:
// Interface for observing changes to the list of available apps.
class Observer {
public:
virtual ~Observer() = default;
// Called when the list of available apps that will be returned by
// GetAvailableApps() changes or when |play_store_enabled_| changes state.
virtual void OnAvailableNoteTakingAppsUpdated() = 0;
// Called when the preferred note taking app (or its properties) in
// |profile| is updated.
virtual void OnPreferredNoteTakingAppUpdated(Profile* profile) = 0;
};
// Describes the result of an attempt to launch a note-taking app. Values must
// not be renumbered, as this is used by histogram metrics.
enum class LaunchResult {
// A Chrome app was launched successfully.
CHROME_SUCCESS = 0,
// The requested Chrome app was unavailable.
CHROME_APP_MISSING = 1,
// An Android app was launched successfully.
ANDROID_SUCCESS = 2,
// An Android app couldn't be launched due to the profile not being allowed
// to use ARC.
ANDROID_NOT_SUPPORTED_BY_PROFILE = 3,
// An Android app couldn't be launched due to ARC not running.
ANDROID_NOT_RUNNING = 4,
// Removed: An Android app couldn't be launched due to a failure to convert
// the supplied path to an ARC URL.
// ANDROID_FAILED_TO_CONVERT_PATH = 5,
// No attempt was made due to a preferred app not being specified.
NO_APP_SPECIFIED = 6,
// No Android or Chrome apps were available.
NO_APPS_AVAILABLE = 7,
// A web app was launched successfully.
WEB_APP_SUCCESS = 8,
// The requested web app was unavailable.
WEB_APP_MISSING = 9,
// Unable to find an internal display.
NO_INTERNAL_DISPLAY_FOUND = 10,
// This value must remain last and should be incremented when a new reason
// is inserted.
MAX = 11,
};
// Callback used to launch a Chrome app.
using LaunchChromeAppCallback =
base::RepeatingCallback<void(content::BrowserContext* context,
const extensions::Extension*,
extensions::api::app_runtime::ActionData)>;
// Intent action used to launch Android apps.
static const char kIntentAction[];
// Extension IDs for the development and released versions of the Google Keep
// Chrome app.
static const char kDevKeepExtensionId[];
static const char kProdKeepExtensionId[];
// Web app IDs for testing and development versions of note-taking web apps.
static const char kNoteTakingWebAppIdTest[];
static const char kNoteTakingWebAppIdDev[];
// Names of histograms.
static const char kPreferredLaunchResultHistogramName[];
static const char kDefaultLaunchResultHistogramName[];
static void Initialize();
static void Shutdown();
static NoteTakingHelper* Get();
NoteTakingHelper(const NoteTakingHelper&) = delete;
NoteTakingHelper& operator=(const NoteTakingHelper&) = delete;
bool play_store_enabled() const { return play_store_enabled_; }
bool android_apps_received() const { return android_apps_received_; }
void set_launch_chrome_app_callback_for_test(
const LaunchChromeAppCallback& callback) {
launch_chrome_app_callback_ = callback;
}
// Adds or removes an observer.
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
// Inform the NoteTakingHelper that the given app was updated. May trigger
// notifications to observers.
void NotifyAppUpdated(Profile* profile, const std::string& app_id);
// Returns a list of available note-taking apps, in the order they should be
// shown in UI.
std::vector<NoteTakingAppInfo> GetAvailableApps(Profile* profile);
// Returns the ID of the preferred note-taking app. Empty if uninstalled or
// not set.
std::string GetPreferredAppId(Profile* profile);
// Sets the preferred note-taking app. |app_id| is a value from a
// NoteTakingAppInfo object.
void SetPreferredApp(Profile* profile, const std::string& app_id);
// Sets whether the preferred note taking app is allowed to run on the lock
// screen.
// Returns whether the app status changed.
bool SetPreferredAppEnabledOnLockScreen(Profile* profile, bool enabled);
// Returns true if an app that can be used to take notes is available. UI
// surfaces that call LaunchAppForNewNote() should be hidden otherwise.
bool IsAppAvailable(Profile* profile);
// Launches the note-taking app to create a new note. IsAppAvailable() must
// be called first.
void LaunchAppForNewNote(Profile* profile);
// arc::ArcIntentHelperObserver:
void OnIntentFiltersUpdated(
const std::optional<std::string>& package_name) override;
// arc::ArcSessionManagerObserver:
void OnArcPlayStoreEnabledChanged(bool enabled) override;
// ProfileManagerObserver:
void OnProfileAdded(Profile* profile) override;
void OnProfileManagerDestroying() override;
NoteTakingControllerClient* GetNoteTakingControllerClientForTesting() {
return note_taking_controller_client_.get();
}
private:
NoteTakingHelper();
~NoteTakingHelper() override;
// Queries and returns the app IDs of note-taking Chrome/web apps that are
// installed, enabled, and allowed for |profile|.
std::vector<std::string> GetNoteTakingAppIds(Profile* profile) const;
// Requests a list of Android note-taking apps from ARC.
void UpdateAndroidApps();
// Handles ARC's response to an earlier UpdateAndroidApps() call.
void OnGotAndroidApps(std::vector<arc::mojom::IntentHandlerInfoPtr> handlers);
// Helper method that launches |app_id| (either an Android package name or a
// Chrome extension ID) to create a new note. Returns the attempt's result.
LaunchResult LaunchAppInternal(Profile* profile, const std::string& app_id);
// apps::AppRegistryCache::Observer
void OnAppUpdate(const apps::AppUpdate& update) override;
void OnAppRegistryCacheWillBeDestroyed(
apps::AppRegistryCache* cache) override;
// True iff Play Store is enabled (i.e. per the checkbox on the settings
// page). Note that ARC may not be fully started yet when this is true, but it
// is expected to start eventually. Similarly, ARC may not be fully shut down
// yet when this is false, but will be eventually.
bool play_store_enabled_ = false;
// This is set to true after |android_apps_| is updated.
bool android_apps_received_ = false;
// Callback used to launch Chrome apps. Can be overridden for tests.
LaunchChromeAppCallback launch_chrome_app_callback_;
// IDs of allowed (but not necessarily installed) Chrome apps or web apps for
// note-taking, in the order in which they're chosen if the user hasn't
// expressed a preference. Explicitly set by command-line and a default
// hard-coded list.
std::vector<std::string> force_allowed_app_ids_;
// Cached information about available Android note-taking apps.
std::vector<NoteTakingAppInfo> android_apps_;
// Observes ArcIntentHelper for changes to Android intent filters.
// TODO(crbug.com/40228788): Remove when App Service publishes Android Apps
// with note-taking intent.
base::ScopedMultiSourceObservation<arc::ArcIntentHelperBridge,
arc::ArcIntentHelperObserver>
arc_intent_helper_observations_{this};
// Obseves App Registry for all profiles with an App Registry.
base::ScopedMultiSourceObservation<apps::AppRegistryCache,
apps::AppRegistryCache::Observer>
app_registry_observations_{this};
base::ScopedObservation<ProfileManager, ProfileManagerObserver>
profile_manager_observation_{this};
base::ObserverList<Observer>::Unchecked observers_;
std::unique_ptr<NoteTakingControllerClient> note_taking_controller_client_;
base::WeakPtrFactory<NoteTakingHelper> weak_ptr_factory_{this};
};
} // namespace ash
#endif // CHROME_BROWSER_ASH_NOTE_TAKING_NOTE_TAKING_HELPER_H_