blob: 582c7dabf96662ecb564b95bda089772a7cc7354 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/background/glic/glic_background_mode_manager.h"
#include <memory>
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_functions.h"
#include "chrome/browser/background/glic/glic_controller.h"
#include "chrome/browser/background/glic/glic_launcher_configuration.h"
#include "chrome/browser/background/glic/glic_status_icon.h"
#include "chrome/browser/background/startup_launch_manager.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/glic/host/glic.mojom.h"
#include "chrome/browser/glic/public/glic_enabling.h"
#include "chrome/browser/glic/public/glic_keyed_service_factory.h"
#include "chrome/browser/global_features.h"
#include "chrome/browser/profiles/nuke_profile_directory_utils.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "components/keep_alive_registry/keep_alive_registry.h"
#include "components/keep_alive_registry/keep_alive_types.h"
#include "components/keep_alive_registry/scoped_keep_alive.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.h"
namespace glic {
GlicBackgroundModeManager::GlicBackgroundModeManager(StatusTray* status_tray)
: configuration_(std::make_unique<GlicLauncherConfiguration>(this)),
controller_(std::make_unique<GlicController>()),
status_tray_(status_tray),
enabled_pref_(GlicLauncherConfiguration::IsEnabled()),
expected_registered_hotkey_(
GlicLauncherConfiguration::GetGlobalHotkey()) {
g_browser_process->profile_manager()->AddObserver(this);
// Start tracking any profiles that already exist.
for (auto* profile :
g_browser_process->profile_manager()->GetLoadedProfiles()) {
OnProfileAdded(profile);
}
EnableLaunchOnStartup(enabled_pref_);
UpdateState();
}
GlicBackgroundModeManager::~GlicBackgroundModeManager() {
g_browser_process->profile_manager()->RemoveObserver(this);
}
GlicBackgroundModeManager* GlicBackgroundModeManager::GetInstance() {
return g_browser_process->GetFeatures()->glic_background_mode_manager();
}
void GlicBackgroundModeManager::OnEnabledChanged(bool enabled) {
if (enabled_pref_ == enabled) {
return;
}
enabled_pref_ = enabled;
UpdateState();
EnableLaunchOnStartup(enabled_pref_);
}
void GlicBackgroundModeManager::OnGlobalHotkeyChanged(ui::Accelerator hotkey) {
if (expected_registered_hotkey_ == hotkey) {
return;
}
expected_registered_hotkey_ = hotkey;
UpdateState();
}
void GlicBackgroundModeManager::OnKeyPressed(
const ui::Accelerator& accelerator) {
CHECK(accelerator == actual_registered_hotkey_);
CHECK(actual_registered_hotkey_ == expected_registered_hotkey_);
controller_->Toggle(mojom::InvocationSource::kOsHotkey);
// Record hotkey usage.
const ui::Accelerator default_hotkey =
GlicLauncherConfiguration::GetDefaultHotkey();
base::UmaHistogramEnumeration("Glic.Usage.Hotkey",
accelerator == default_hotkey
? glic::HotkeyUsage::kDefault
: glic::HotkeyUsage::kCustom);
}
void GlicBackgroundModeManager::ExecuteCommand(
const std::string& accelerator_group_id,
const std::string& command_id) {
// TODO(crbug.com/385194502): Handle Linux.
}
void GlicBackgroundModeManager::OnProfileAdded(Profile* profile) {
auto* service = GlicKeyedServiceFactory::GetGlicKeyedService(profile);
if (!service) {
return;
}
// Recompute whether the background launcher should change state based on the
// updated policy and FRE completion state.
GlicEnabling& enabling = service->enabling();
profile_enabled_subscriptions_.emplace(
profile, enabling.RegisterAllowedChanged(
base::BindRepeating(&GlicBackgroundModeManager::UpdateState,
weak_ptr_factory_.GetWeakPtr())));
profile_consent_subscriptions_.emplace(
profile, enabling.RegisterOnConsentChanged(
base::BindRepeating(&GlicBackgroundModeManager::UpdateState,
weak_ptr_factory_.GetWeakPtr())));
auto [it, inserted] = profile_observers_.emplace(profile, this);
it->second.Observe(profile);
// If a profile is added when not in background mode, check if it can now be
// entered.
if (!status_icon_) {
CHECK(!keep_alive_);
UpdateState();
}
}
void GlicBackgroundModeManager::OnProfileWillBeDestroyed(Profile* profile) {
profile_observers_.erase(profile);
profile_enabled_subscriptions_.erase(profile);
profile_consent_subscriptions_.erase(profile);
// If a profile is removed while in background mode, check if it must now be
// exited.
if (status_icon_) {
UpdateState();
}
}
void GlicBackgroundModeManager::Shutdown() {
CHECK(g_browser_process->profile_manager());
g_browser_process->profile_manager()->RemoveObserver(this);
}
void GlicBackgroundModeManager::EnterBackgroundMode() {
KeepAliveRegistry* const keep_alive_registry =
KeepAliveRegistry::GetInstance();
if (!keep_alive_ && keep_alive_registry &&
!keep_alive_registry->IsShuttingDown()) {
keep_alive_ = std::make_unique<ScopedKeepAlive>(
KeepAliveOrigin::GLIC_LAUNCHER, KeepAliveRestartOption::ENABLED);
}
if (!status_icon_) {
status_icon_ =
std::make_unique<GlicStatusIcon>(controller_.get(), status_tray_);
}
}
void GlicBackgroundModeManager::ExitBackgroundMode() {
status_icon_.reset();
keep_alive_.reset();
}
void GlicBackgroundModeManager::EnableLaunchOnStartup(bool should_launch) {
#if BUILDFLAG(IS_WIN)
if (should_launch) {
StartupLaunchManager::GetInstance()->RegisterLaunchOnStartup(
StartupLaunchReason::kGlic);
} else {
StartupLaunchManager::GetInstance()->UnregisterLaunchOnStartup(
StartupLaunchReason::kGlic);
}
#endif
}
void GlicBackgroundModeManager::RegisterHotkey(ui::Accelerator updated_hotkey) {
CHECK(!updated_hotkey.IsEmpty());
auto* const global_accelerator_listener =
ui::GlobalAcceleratorListener::GetInstance();
if (global_accelerator_listener) {
const bool shortcut_handling_suspended =
global_accelerator_listener->IsShortcutHandlingSuspended();
// Re-enable shortcut handling to allow the global accelerator listener to
// register the hotkey.
global_accelerator_listener->SetShortcutHandlingSuspended(false);
if (global_accelerator_listener->RegisterAccelerator(updated_hotkey,
this)) {
actual_registered_hotkey_ = updated_hotkey;
}
global_accelerator_listener->SetShortcutHandlingSuspended(
shortcut_handling_suspended);
}
}
void GlicBackgroundModeManager::UnregisterHotkey() {
auto* const global_accelerator_listener =
ui::GlobalAcceleratorListener::GetInstance();
if (global_accelerator_listener && !actual_registered_hotkey_.IsEmpty()) {
global_accelerator_listener->UnregisterAccelerator(
actual_registered_hotkey_, this);
}
actual_registered_hotkey_ = ui::Accelerator();
}
void GlicBackgroundModeManager::UpdateState() {
UnregisterHotkey();
bool background_mode_enabled = enabled_pref_ && IsEnabledInAnyLoadedProfile();
if (background_mode_enabled) {
EnterBackgroundMode();
if (!expected_registered_hotkey_.IsEmpty()) {
RegisterHotkey(expected_registered_hotkey_);
}
} else {
ExitBackgroundMode();
}
if (status_icon_) {
status_icon_->UpdateHotkey(actual_registered_hotkey_);
}
}
bool GlicBackgroundModeManager::IsEnabledInAnyLoadedProfile() {
for (const auto& pair : profile_observers_) {
Profile* profile = pair.first;
if (!IsProfileDirectoryMarkedForDeletion(profile->GetPath()) &&
glic::GlicEnabling::IsEnabledAndConsentForProfile(profile)) {
return true;
}
}
return false;
}
} // namespace glic