blob: 281ae388887ad926c1a45b5d3ee8e39475990cbd [file] [log] [blame]
// Copyright 2021 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 "chrome/browser/ui/ash/projector/projector_client_impl.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/public/cpp/projector/annotator_tool.h"
#include "ash/public/cpp/projector/projector_controller.h"
#include "ash/public/cpp/projector/projector_new_screencast_precondition.h"
#include "ash/webui/projector_app/annotator_message_handler.h"
#include "ash/webui/projector_app/projector_app_client.h"
#include "ash/webui/projector_app/public/cpp/projector_app_constants.h"
#include "base/bind.h"
#include "chrome/browser/ash/system_web_apps/system_web_app_manager.h"
#include "chrome/browser/ash/system_web_apps/types/system_web_app_type.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/speech/on_device_speech_recognizer.h"
#include "chrome/browser/ui/ash/projector/projector_utils.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/web_applications/system_web_app_ui_utils.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/browser/web_applications/web_app_sync_bridge.h"
#include "chromeos/login/login_state/login_state.h"
#include "components/soda/soda_installer.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "media/base/media_switches.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/views/controls/webview/webview.h"
#include "url/gurl.h"
namespace {
inline const std::string& GetLocale() {
return g_browser_process->GetApplicationLocale();
}
} // namespace
// static
void ProjectorClientImpl::InitForProjectorAnnotator(views::WebView* web_view) {
if (!ash::features::IsProjectorAnnotatorEnabled())
return;
web_view->LoadInitialURL(GURL(ash::kChromeUITrustedAnnotatorAppUrl));
}
ProjectorClientImpl::ProjectorClientImpl(ash::ProjectorController* controller)
: controller_(controller) {
controller_->SetClient(this);
session_manager::SessionManager* session_manager =
session_manager::SessionManager::Get();
if (session_manager)
session_observation_.Observe(session_manager);
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
if (user_manager)
session_state_observation_.Observe(user_manager);
}
ProjectorClientImpl::ProjectorClientImpl()
: ProjectorClientImpl(ash::ProjectorController::Get()) {}
ProjectorClientImpl::~ProjectorClientImpl() {
controller_->SetClient(nullptr);
}
void ProjectorClientImpl::StartSpeechRecognition() {
// ProjectorController should only request for speech recognition after it
// has been informed that recognition is available.
// TODO(crbug.com/1165437): Dynamically determine language code.
DCHECK(OnDeviceSpeechRecognizer::IsOnDeviceSpeechRecognizerAvailable(
GetLocale()));
DCHECK_EQ(speech_recognizer_.get(), nullptr);
recognizer_status_ = SPEECH_RECOGNIZER_OFF;
speech_recognizer_ = std::make_unique<OnDeviceSpeechRecognizer>(
weak_ptr_factory_.GetWeakPtr(), ProfileManager::GetActiveUserProfile(),
GetLocale(), /*recognition_mode_ime=*/false,
/*enable_formatting=*/true);
}
void ProjectorClientImpl::StopSpeechRecognition() {
speech_recognizer_->Stop();
}
bool ProjectorClientImpl::GetDriveFsMountPointPath(
base::FilePath* result) const {
if (!IsDriveFsMounted())
return false;
if (ash::ProjectorController::AreExtendedProjectorFeaturesDisabled()) {
auto* profile = ProfileManager::GetActiveUserProfile();
DCHECK(profile);
DownloadPrefs* download_prefs = DownloadPrefs::FromBrowserContext(
ProfileManager::GetActiveUserProfile());
*result = download_prefs->GetDefaultDownloadDirectoryForProfile();
return true;
}
drive::DriveIntegrationService* integration_service =
GetDriveIntegrationServiceForActiveProfile();
*result = integration_service->GetMountPointPath();
return true;
}
bool ProjectorClientImpl::IsDriveFsMounted() const {
if (!chromeos::LoginState::Get()->IsUserLoggedIn())
return false;
if (ash::ProjectorController::AreExtendedProjectorFeaturesDisabled()) {
// Return true when extended projector features are disabled. Use download
// folder for Projector storage.
return true;
}
drive::DriveIntegrationService* integration_service =
GetDriveIntegrationServiceForActiveProfile();
return integration_service && integration_service->IsMounted();
}
bool ProjectorClientImpl::IsDriveFsMountFailed() const {
drive::DriveIntegrationService* integration_service =
GetDriveIntegrationServiceForActiveProfile();
return integration_service && integration_service->mount_failed();
}
void ProjectorClientImpl::OpenProjectorApp() const {
auto* profile = ProfileManager::GetActiveUserProfile();
web_app::LaunchSystemWebAppAsync(profile, ash::SystemWebAppType::PROJECTOR);
}
void ProjectorClientImpl::MinimizeProjectorApp() const {
auto* profile = ProfileManager::GetActiveUserProfile();
auto* browser = web_app::FindSystemWebAppBrowser(
profile, ash::SystemWebAppType::PROJECTOR);
if (browser)
browser->window()->Minimize();
}
void ProjectorClientImpl::CloseProjectorApp() const {
auto* profile = ProfileManager::GetActiveUserProfile();
auto* browser = web_app::FindSystemWebAppBrowser(
profile, ash::SystemWebAppType::PROJECTOR);
if (browser)
browser->window()->Close();
}
void ProjectorClientImpl::OnNewScreencastPreconditionChanged(
const ash::NewScreencastPrecondition& precondition) const {
ash::ProjectorAppClient* app_client = ash::ProjectorAppClient::Get();
if (app_client)
app_client->OnNewScreencastPreconditionChanged(precondition);
}
void ProjectorClientImpl::SetAnnotatorMessageHandler(
ash::AnnotatorMessageHandler* handler) {
message_handler_ = handler;
}
void ProjectorClientImpl::OnSpeechResult(
const std::u16string& text,
bool is_final,
const absl::optional<media::SpeechRecognitionResult>& full_result) {
DCHECK(full_result.has_value());
controller_->OnTranscription(full_result.value());
}
void ProjectorClientImpl::OnSpeechRecognitionStateChanged(
SpeechRecognizerStatus new_state) {
if (new_state == SPEECH_RECOGNIZER_ERROR) {
speech_recognizer_.reset();
recognizer_status_ = SPEECH_RECOGNIZER_OFF;
controller_->OnTranscriptionError();
} else if (new_state == SPEECH_RECOGNIZER_READY) {
if (recognizer_status_ == SPEECH_RECOGNIZER_OFF && speech_recognizer_) {
// The SpeechRecognizer was initialized after being created, and
// is ready to start recognizing speech.
speech_recognizer_->Start();
}
}
recognizer_status_ = new_state;
}
void ProjectorClientImpl::OnSpeechRecognitionStopped() {
speech_recognizer_.reset();
recognizer_status_ = SPEECH_RECOGNIZER_OFF;
controller_->OnSpeechRecognitionStopped();
}
void ProjectorClientImpl::SetTool(const ash::AnnotatorTool& tool) {
message_handler_->SetTool(tool);
}
// TODO(b/220202359): Implement undo.
void ProjectorClientImpl::Undo() {}
// TODO(b/220202359): Implement redo.
void ProjectorClientImpl::Redo() {}
void ProjectorClientImpl::Clear() {
message_handler_->Clear();
}
void ProjectorClientImpl::OnFileSystemMounted() {
OnNewScreencastPreconditionChanged(
controller_->GetNewScreencastPrecondition());
}
void ProjectorClientImpl::OnFileSystemBeingUnmounted() {
OnNewScreencastPreconditionChanged(
controller_->GetNewScreencastPrecondition());
}
void ProjectorClientImpl::OnFileSystemMountFailed() {
OnNewScreencastPreconditionChanged(
controller_->GetNewScreencastPrecondition());
}
void ProjectorClientImpl::OnUserProfileLoaded(const AccountId& account_id) {
MaybeSwitchDriveIntegrationServiceObservation();
}
void ProjectorClientImpl::OnUserSessionStarted(bool is_primary_user) {
if (!is_primary_user || !pref_change_registrar_.IsEmpty())
return;
Profile* profile = ProfileManager::GetActiveUserProfile();
pref_change_registrar_.Init(profile->GetPrefs());
// TOOD(b/232043809): Consider using the disabled system feature policy
// instead.
pref_change_registrar_.Add(
ash::prefs::kProjectorAllowByPolicy,
base::BindRepeating(&ProjectorClientImpl::OnEnablementPolicyChanged,
base::Unretained(this)));
pref_change_registrar_.Add(
ash::prefs::kProjectorDogfoodForFamilyLinkEnabled,
base::BindRepeating(&ProjectorClientImpl::OnEnablementPolicyChanged,
base::Unretained(this)));
}
void ProjectorClientImpl::ActiveUserChanged(user_manager::User* active_user) {
// After user login, the first ActiveUserChanged() might be called before
// profile is loaded.
if (active_user->is_profile_created())
MaybeSwitchDriveIntegrationServiceObservation();
}
void ProjectorClientImpl::MaybeSwitchDriveIntegrationServiceObservation() {
auto* profile = ProfileManager::GetActiveUserProfile();
if (!IsProjectorAllowedForProfile(profile))
return;
drive::DriveIntegrationService* drive_service =
GetDriveIntegrationServiceForActiveProfile();
if (!drive_service || drive_observation_.IsObservingSource(drive_service))
return;
drive_observation_.Reset();
drive_observation_.Observe(drive_service);
}
void ProjectorClientImpl::OnEnablementPolicyChanged() {
Profile* profile = ProfileManager::GetActiveUserProfile();
ash::SystemWebAppManager* swa_manager =
ash::SystemWebAppManager::Get(profile);
const bool is_installed =
swa_manager &&
swa_manager->IsSystemWebApp(ash::kChromeUITrustedProjectorSwaAppId);
// We can't enable or disable the app if it's not already installed.
if (!is_installed)
return;
const bool is_enabled = IsProjectorAppEnabled(profile);
// The policy has changed to disallow the Projector app. Since we can't
// uninstall the Projector SWA until the user signs out and back in, we should
// close and disable the app for this current session.
if (!is_enabled)
CloseProjectorApp();
auto* web_app_provider = web_app::WebAppProvider::GetForWebApps(profile);
web_app_provider->sync_bridge().SetAppIsDisabled(
ash::kChromeUITrustedProjectorSwaAppId, !is_enabled);
}