blob: e1e93fa789ecda676158f866e360635700964027 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/scoped_observer.h"
#include "chrome/browser/chromeos/arc/arc_session_manager.h"
#include "components/arc/common/intent_helper.mojom.h"
#include "components/arc/intent_helper/arc_intent_helper_observer.h"
#include "components/prefs/pref_change_registrar.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_source.h"
#include "extensions/browser/extension_registry_observer.h"
#include "extensions/common/extension.h"
class Profile;
namespace base {
class FilePath;
} // namespace base
namespace content {
class BrowserContext;
} // namespace content
namespace extensions {
class Extension;
class ExtensionRegistry;
namespace api {
namespace app_runtime {
struct ActionData;
} // namespace app_runtime
} // namespace api
} // namespace extensions
namespace chromeos {
class NoteTakingControllerClient;
// Describes an app's level of support for lock screen enabled note taking.
// IMPORTANT: These constants are used in settings UI, so be careful about
// reordering/adding/removing items.
enum class NoteTakingLockScreenSupport {
// The app does not support note taking on lock screen.
kNotSupported = 0,
// The app supports lock screen note taking, but is not allowed to run on the
// lock screen due to policy settings.
kNotAllowedByPolicy = 1,
// The app supports note taking on lock screen, but is not enabled as a
// lock screen note taking app by the user. This state implies that the user
// can be offered to enable this app as the lock screen note taking handler.
kSupported = 2,
// The app is enabled by the user to run as a note taking handler on the lock
// screen. Note that, while more than one app can be in enabled state at a
// same time, currently only the preferred note taking app will be launchable
// from the lock screen UI.
kEnabled = 3,
// 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).
std::string app_id;
// True if this is the preferred note-taking app.
bool preferred;
// Whether the app supports taking notes on Chrome OS lock screen. Note that
// this ability is guarded by enable-lock-screen-apps feature flag, and
// whitelisted to Keep apps.
NoteTakingLockScreenSupport lock_screen_support;
using NoteTakingAppInfos = std::vector<NoteTakingAppInfo>;
// Singleton class used to launch a note-taking app.
class NoteTakingHelper : public arc::ArcIntentHelperObserver,
public arc::ArcSessionManager::Observer,
public content::NotificationObserver,
public extensions::ExtensionRegistryObserver {
// Interface for observing changes to the list of available apps.
class Observer {
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.
// The requested Chrome app was unavailable.
// An Android app was launched successfully.
// An Android app couldn't be launched due to the profile not being allowed
// to use ARC.
// An Android app couldn't be launched due to ARC not running.
// An Android app couldn't be launched due to a failure to convert the
// supplied path to an ARC URL.
// No attempt was made due to a preferred app not being specified.
// No Android or Chrome apps were available.
// This value must remain last and should be incremented when a new reason
// is inserted.
MAX = 8,
// Callback used to launch a Chrome app.
using LaunchChromeAppCallback = base::Callback<void(
content::BrowserContext* context,
const extensions::Extension*,
const base::FilePath&)>;
// 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[];
// Names of histograms.
static const char kPreferredLaunchResultHistogramName[];
static const char kDefaultLaunchResultHistogramName[];
static void Initialize();
static void Shutdown();
static NoteTakingHelper* Get();
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);
// Returns a list of available note-taking apps.
std::vector<NoteTakingAppInfo> GetAvailableApps(Profile* profile);
// Returns the preferred app info, if the preferred app exists and is a Chrome
// app.
std::unique_ptr<NoteTakingAppInfo> GetPreferredChromeAppInfo(
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);
// Enables or disables preferred note taking apps from running 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, optionally additionally
// passing a file (|path| may be empty). IsAppAvailable() must be called
// first.
void LaunchAppForNewNote(Profile* profile, const base::FilePath& path);
// arc::ArcIntentHelperObserver:
void OnIntentFiltersUpdated() override;
// arc::ArcSessionManager::Observer:
void OnArcPlayStoreEnabledChanged(bool enabled) override;
// Sets the profile which supports note taking apps on the lock screen.
void SetProfileWithEnabledLockScreenApps(Profile* profile);
NoteTakingControllerClient* GetNoteTakingControllerClientForTesting() {
return note_taking_controller_client_.get();
// The state of app ID whitelist cache (used for determining the state of
// note-taking apps whtielisted for the lock screen).
enum class AppWhitelistState {
// The whitelist value has not yet been determined.
// The app ID whitelist does not exist in the profile.
// The app ID whitelist exists in the profile.
~NoteTakingHelper() override;
// Returns true if |extension| is a whitelisted note-taking app and false
// otherwise.
bool IsWhitelistedChromeApp(const extensions::Extension* extension) const;
// Queries and returns all installed and enabled whitelisted Chrome
// note-taking apps for |profile|.
std::vector<const extensions::Extension*> GetChromeApps(
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 with an optional attached file at
// |path|. Returns the attempt's result.
LaunchResult LaunchAppInternal(Profile* profile,
const std::string& app_id,
const base::FilePath& path);
// content::NotificationObserver:
void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) override;
// extensions::ExtensionRegistryObserver:
void OnExtensionLoaded(content::BrowserContext* browser_context,
const extensions::Extension* extension) override;
void OnExtensionUnloaded(content::BrowserContext* browser_context,
const extensions::Extension* extension,
extensions::UnloadedExtensionReason reason) override;
void OnShutdown(extensions::ExtensionRegistry* registry) override;
// Determines the state of the |app|'s support for lock screen note taking.
// |profile| - The profile in which the app is installed.
NoteTakingLockScreenSupport GetLockScreenSupportForChromeApp(
Profile* profile,
const extensions::Extension* app);
// Called when kNoteTakingAppsLockScreenWhitelist pref changes for
// |profile_with_enabled_lock_screen_apps_|.
void OnAllowedNoteTakingAppsChanged();
// Updates the cached whitelist of note-taking apps allowed on the lock
// screen - it sets |lock_screen_whitelist_state_| and
// |lock_screen_apps_allowed_by_policy_| to values appropriate for the current
// |profile_with_enabled_lock_screen_apps_| state.
void UpdateLockScreenAppsWhitelistState();
// 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_;
// Extension IDs of whitelisted (but not necessarily installed) Chrome
// note-taking apps in the order in which they're chosen if the user hasn't
// expressed a preference.
std::vector<extensions::ExtensionId> whitelisted_chrome_app_ids_;
// Cached information about available Android note-taking apps.
NoteTakingAppInfos android_apps_;
// Tracks ExtensionRegistry observation for different profiles.
// The profile for which lock screen apps are enabled,
Profile* profile_with_enabled_lock_screen_apps_ = nullptr;
// The current AppWhitelistState for lock screen note taking in
// |profile_with_enabled_lock_screen_apps_|. If kAppsWhitelisted,
// |lock_screen_apps_allowed_by_policy_| should contain the set of whitelisted
// app IDs.
AppWhitelistState lock_screen_whitelist_state_ =
// If |lock_screen_whitelist_state_| is kAppsWhitelisted, contains all app
// IDs that are allowed to handle new-note action on the lock screen. The set
// should only be used for apps from |profile_with_enabled_lock_screen_apps_|
// and when |lock_screen_whitelist_state_| equals kAppsWhitelisted.
std::set<std::string> lock_screen_apps_allowed_by_policy_;
// Tracks kNoteTakingAppsLockScreenWhitelist pref for the profile for which
// lock screen apps are enabled.
PrefChangeRegistrar pref_change_registrar_;
base::ObserverList<Observer>::Unchecked observers_;
content::NotificationRegistrar registrar_;
std::unique_ptr<NoteTakingControllerClient> note_taking_controller_client_;
base::WeakPtrFactory<NoteTakingHelper> weak_ptr_factory_;
} // namespace chromeos