| // Copyright 2021 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/ui/quick_answers/quick_answers_state_ash.h" |
| |
| #include "ash/constants/ash_pref_names.h" |
| #include "ash/session/session_controller_impl.h" |
| #include "ash/shell.h" |
| #include "chromeos/components/kiosk/kiosk_utils.h" |
| #include "chromeos/components/quick_answers/public/cpp/constants.h" |
| #include "chromeos/components/quick_answers/public/cpp/quick_answers_prefs.h" |
| #include "chromeos/components/quick_answers/quick_answers_model.h" |
| #include "chromeos/components/quick_answers/utils/quick_answers_metrics.h" |
| #include "components/language/core/browser/pref_names.h" |
| #include "components/prefs/pref_change_registrar.h" |
| #include "components/prefs/pref_service.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| namespace { |
| |
| using quick_answers::prefs::ConsentStatus; |
| using quick_answers::prefs::kQuickAnswersConsentStatus; |
| using quick_answers::prefs::kQuickAnswersDefinitionEnabled; |
| using quick_answers::prefs::kQuickAnswersEnabled; |
| using quick_answers::prefs::kQuickAnswersNoticeImpressionCount; |
| using quick_answers::prefs::kQuickAnswersTranslationEnabled; |
| using quick_answers::prefs::kQuickAnswersUnitConversionEnabled; |
| |
| } // namespace |
| |
| QuickAnswersStateAsh::QuickAnswersStateAsh() { |
| shell_observation_.Observe(ash::Shell::Get()); |
| |
| auto* session_controller = ash::Shell::Get()->session_controller(); |
| CHECK(session_controller); |
| |
| session_observation_.Observe(session_controller); |
| |
| // Register pref changes if use session already started. |
| if (session_controller->IsActiveUserSessionStarted()) { |
| PrefService* prefs = session_controller->GetPrimaryUserPrefService(); |
| DCHECK(prefs); |
| RegisterPrefChanges(prefs); |
| } |
| } |
| |
| QuickAnswersStateAsh::~QuickAnswersStateAsh() = default; |
| |
| void QuickAnswersStateAsh::OnFirstSessionStarted() { |
| PrefService* prefs = |
| ash::Shell::Get()->session_controller()->GetPrimaryUserPrefService(); |
| RegisterPrefChanges(prefs); |
| } |
| |
| void QuickAnswersStateAsh::OnChromeTerminating() { |
| session_observation_.Reset(); |
| } |
| |
| void QuickAnswersStateAsh::OnShellDestroying() { |
| session_observation_.Reset(); |
| shell_observation_.Reset(); |
| } |
| |
| void QuickAnswersStateAsh::RegisterPrefChanges(PrefService* pref_service) { |
| pref_change_registrar_.reset(); |
| |
| if (!pref_service) { |
| return; |
| } |
| |
| // Register preference changes. |
| pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>(); |
| pref_change_registrar_->Init(pref_service); |
| pref_change_registrar_->Add( |
| kQuickAnswersEnabled, |
| base::BindRepeating(&QuickAnswersStateAsh::UpdateSettingsEnabled, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| kQuickAnswersConsentStatus, |
| base::BindRepeating(&QuickAnswersStateAsh::UpdateConsentStatus, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| kQuickAnswersDefinitionEnabled, |
| base::BindRepeating(&QuickAnswersStateAsh::UpdateDefinitionEnabled, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| kQuickAnswersTranslationEnabled, |
| base::BindRepeating(&QuickAnswersStateAsh::UpdateTranslationEnabled, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| kQuickAnswersUnitConversionEnabled, |
| base::BindRepeating(&QuickAnswersStateAsh::UpdateUnitConversionEnabled, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| language::prefs::kApplicationLocale, |
| base::BindRepeating(&QuickAnswersStateAsh::OnApplicationLocaleReady, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| language::prefs::kPreferredLanguages, |
| base::BindRepeating(&QuickAnswersStateAsh::UpdatePreferredLanguages, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| ash::prefs::kAccessibilitySpokenFeedbackEnabled, |
| base::BindRepeating(&QuickAnswersStateAsh::UpdateSpokenFeedbackEnabled, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| kQuickAnswersNoticeImpressionCount, |
| base::BindRepeating(&QuickAnswersStateAsh::UpdateNoticeImpressionCount, |
| base::Unretained(this))); |
| |
| UpdateSettingsEnabled(); |
| UpdateConsentStatus(); |
| UpdateDefinitionEnabled(); |
| UpdateTranslationEnabled(); |
| UpdateUnitConversionEnabled(); |
| OnApplicationLocaleReady(); |
| UpdatePreferredLanguages(); |
| UpdateSpokenFeedbackEnabled(); |
| UpdateNoticeImpressionCount(); |
| |
| prefs_initialized_ = true; |
| for (auto& observer : observers_) { |
| observer.OnPrefsInitialized(); |
| } |
| |
| quick_answers::RecordFeatureEnabled(IsEnabledAs(FeatureType::kQuickAnswers)); |
| |
| MaybeNotifyEligibilityChanged(); |
| MaybeNotifyIsEnabledChanged(); |
| } |
| |
| void QuickAnswersStateAsh::AsyncWriteConsentUiImpressionCount(int32_t count) { |
| pref_change_registrar_->prefs()->SetInteger( |
| kQuickAnswersNoticeImpressionCount, count); |
| } |
| |
| void QuickAnswersStateAsh::AsyncWriteConsentStatus( |
| ConsentStatus consent_status) { |
| pref_change_registrar_->prefs()->SetInteger(kQuickAnswersConsentStatus, |
| consent_status); |
| } |
| |
| void QuickAnswersStateAsh::AsyncWriteEnabled(bool enabled) { |
| pref_change_registrar_->prefs()->SetBoolean(kQuickAnswersEnabled, enabled); |
| } |
| |
| void QuickAnswersStateAsh::UpdateSettingsEnabled() { |
| auto* prefs = pref_change_registrar_->prefs(); |
| |
| // TODO(b/340628526): modifying a state is error-prone. For example, if a |
| // state is read before a modification happens, a stale state will be read. |
| // Instead, each state (e.g., IsEnabled) should be calculated from other |
| // dependent states (e.g., IsKioskSession). |
| bool settings_enabled = prefs->GetBoolean(kQuickAnswersEnabled); |
| |
| // Quick answers should be disabled for kiosk session. |
| if (chromeos::IsKioskSession()) { |
| settings_enabled = false; |
| prefs->SetBoolean(kQuickAnswersEnabled, false); |
| prefs->SetInteger(kQuickAnswersConsentStatus, ConsentStatus::kRejected); |
| } |
| |
| // If the feature is enforced off by the administrator policy, set the |
| // consented status to rejected. This must be put before the same value return |
| // below as the default value is `false` and we cannot observe |
| // unmanaged-disabled to managed-disabled change. |
| if (!settings_enabled && |
| prefs->IsManagedPreference(quick_answers::prefs::kQuickAnswersEnabled)) { |
| prefs->SetInteger(kQuickAnswersConsentStatus, ConsentStatus::kRejected); |
| } |
| |
| bool turned_on = quick_answers_enabled_.has_value() && |
| !quick_answers_enabled_.value() && settings_enabled; |
| |
| // If the user turn on the Quick Answers in settings, set the consented status |
| // to true. |
| // TODO(b/340628526): move this logic to `QuickAnswersState`. |
| // `QuickAnswersStateAsh` should only have logic unique to ash. Plan is to |
| // make `quick_answers_enabled_` as private and add void |
| // SetQuickAnswersEnabled(bool) as a protected method. |
| if (turned_on) { |
| CHECK(quick_answers_enabled_.has_value()); |
| CHECK(!quick_answers_enabled_.value()); |
| CHECK(settings_enabled); |
| |
| prefs->SetInteger(kQuickAnswersConsentStatus, ConsentStatus::kAccepted); |
| } |
| |
| quick_answers_enabled_ = settings_enabled; |
| MaybeNotifyIsEnabledChanged(); |
| } |
| |
| void QuickAnswersStateAsh::UpdateConsentStatus() { |
| auto consent_status = static_cast<ConsentStatus>( |
| pref_change_registrar_->prefs()->GetInteger(kQuickAnswersConsentStatus)); |
| |
| SetQuickAnswersFeatureConsentStatus(consent_status); |
| } |
| |
| void QuickAnswersStateAsh::UpdateDefinitionEnabled() { |
| auto definition_enabled = pref_change_registrar_->prefs()->GetBoolean( |
| kQuickAnswersDefinitionEnabled); |
| |
| // See a comment of `QuickAnswersState::IsIntentEligible` for the reason of |
| // this is called as eligible instead of enabled. |
| SetIntentEligibilityAsQuickAnswers(quick_answers::Intent::kDefinition, |
| definition_enabled); |
| } |
| |
| void QuickAnswersStateAsh::UpdateTranslationEnabled() { |
| auto translation_enabled = pref_change_registrar_->prefs()->GetBoolean( |
| kQuickAnswersTranslationEnabled); |
| |
| // See a comment of `QuickAnswersState::IsIntentEligible` for the reason of |
| // this is called as eligible instead of enabled. |
| SetIntentEligibilityAsQuickAnswers(quick_answers::Intent::kTranslation, |
| translation_enabled); |
| } |
| |
| void QuickAnswersStateAsh::UpdateUnitConversionEnabled() { |
| auto unit_conversion_enabled = pref_change_registrar_->prefs()->GetBoolean( |
| kQuickAnswersUnitConversionEnabled); |
| |
| // See a comment of `QuickAnswersState::IsIntentEligible` for the reason of |
| // this is called as eligible instead of enabled. |
| SetIntentEligibilityAsQuickAnswers(quick_answers::Intent::kUnitConversion, |
| unit_conversion_enabled); |
| } |
| |
| void QuickAnswersStateAsh::OnApplicationLocaleReady() { |
| auto locale = pref_change_registrar_->prefs()->GetString( |
| language::prefs::kApplicationLocale); |
| |
| if (locale.empty()) { |
| return; |
| } |
| |
| // We should not directly use the pref locale, resolve the generic locale name |
| // to one of the locally defined ones first. |
| std::string resolved_locale; |
| bool resolve_success = |
| l10n_util::CheckAndResolveLocale(locale, &resolved_locale, |
| /*perform_io=*/false); |
| DCHECK(resolve_success); |
| |
| if (resolved_application_locale_ == resolved_locale) { |
| return; |
| } |
| resolved_application_locale_ = resolved_locale; |
| |
| for (auto& observer : observers_) { |
| observer.OnApplicationLocaleReady(resolved_locale); |
| } |
| |
| MaybeNotifyEligibilityChanged(); |
| } |
| |
| void QuickAnswersStateAsh::UpdatePreferredLanguages() { |
| auto preferred_languages = pref_change_registrar_->prefs()->GetString( |
| language::prefs::kPreferredLanguages); |
| |
| preferred_languages_ = preferred_languages; |
| |
| for (auto& observer : observers_) { |
| observer.OnPreferredLanguagesChanged(preferred_languages); |
| } |
| } |
| |
| void QuickAnswersStateAsh::UpdateSpokenFeedbackEnabled() { |
| auto spoken_feedback_enabled = pref_change_registrar_->prefs()->GetBoolean( |
| ash::prefs::kAccessibilitySpokenFeedbackEnabled); |
| |
| spoken_feedback_enabled_ = spoken_feedback_enabled; |
| } |
| |
| void QuickAnswersStateAsh::UpdateNoticeImpressionCount() { |
| consent_ui_impression_count_ = pref_change_registrar_->prefs()->GetInteger( |
| kQuickAnswersNoticeImpressionCount); |
| } |