File: quick_answers_state.h

package info (click to toggle)
chromium 139.0.7258.127-2
  • links: PTS, VCS
  • area: main
  • in suites: forky
  • size: 6,122,156 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (280 lines) | stat: -rw-r--r-- 11,185 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
// 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.

#ifndef CHROMEOS_COMPONENTS_QUICK_ANSWERS_PUBLIC_CPP_QUICK_ANSWERS_STATE_H_
#define CHROMEOS_COMPONENTS_QUICK_ANSWERS_PUBLIC_CPP_QUICK_ANSWERS_STATE_H_

#include <memory>
#include <string>
#include <string_view>

#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/types/expected.h"
#include "chromeos/components/magic_boost/public/cpp/magic_boost_state.h"
#include "chromeos/components/quick_answers/public/cpp/constants.h"
#include "chromeos/components/quick_answers/public/cpp/quick_answers_prefs.h"

// TODO(b/340628526): Put this under quick_answers namespace.

// The consent will appear up to a total of 6 times.
constexpr int kConsentImpressionCap = 6;
// The consent need to show for at least 1 second to be counted.
constexpr int kConsentImpressionMinimumDuration = 1;

// Consent result of the consent-view.
enum class ConsentResultType {
  // When user clicks on the "Allow" button.
  kAllow = 0,
  // When user clicks on the "No thanks" button.
  kNoThanks = 1,
  // When user dismisses or ignores the consent-view.
  kDismiss = 2
};

// A checked observer which receives Quick Answers state change.
class QuickAnswersStateObserver : public base::CheckedObserver {
 public:
  virtual void OnFeatureTypeChanged() {}
  virtual void OnSettingsEnabled(bool enabled) {}
  virtual void OnConsentStatusUpdated(
      quick_answers::prefs::ConsentStatus status) {}
  virtual void OnApplicationLocaleReady(const std::string& locale) {}
  virtual void OnPreferredLanguagesChanged(
      const std::string& preferred_languages) {}
  virtual void OnEligibilityChanged(bool eligible) {}

  // TODO(b/340628526): Delete this method. Each value observer is called once a
  // pref gets initialized.
  virtual void OnPrefsInitialized() {}
};

// `QuickAnswersState` manages states related to Quick Answers as a capability.
// `QuickAnswersState` allows you to query states of Quick Answers capability in
// a specified feature context, e.g., check if Quick Answers capability is
// available under Hmr feature. `QuickAnswersState` expect that `FeatureType`
// does not change in a session.
//
// Terminology:
// - Quick Answers capability: a capability where a user can find definition,
//   translation, unit conversion of a right-clicked text.
// - Quick Answers feature: a feature called Quick Answers. It provides Quick
//   Answers capabpility.
// - Hmr feature: a feature called Hmr. It provides Mahi and Quick Answers
//   capability.
class QuickAnswersState : chromeos::MagicBoostState::Observer {
 public:
  enum class FeatureType {
    kHmr,
    kQuickAnswers,
  };

  enum class Error {
    kUninitialized,
  };

  static QuickAnswersState* Get();

  static FeatureType GetFeatureType();

  // Accessor methods. Those methods handle error cases (Error::kUninitialized,
  // etc) in a fail-safe way.
  static bool IsEligible();
  static bool IsEligibleAs(FeatureType feature_type);
  static bool IsEnabled();
  static bool IsEnabledAs(FeatureType feature_type);
  // `GetConsentStatus` returns `base::expected` instead of falling back to a
  // fail-safe value. `kUnknown` is not a desired fallback value for some cases.
  static base::expected<quick_answers::prefs::ConsentStatus,
                        QuickAnswersState::Error>
  GetConsentStatus();
  static base::expected<quick_answers::prefs::ConsentStatus,
                        QuickAnswersState::Error>
  GetConsentStatusAs(FeatureType feature_type);
  // Intent generation can be done before a feature is enabled to show a user
  // consent UI. Use a word eligible instead of enabled to make it clear that
  // it's not gated by `IsEnabled`.
  static bool IsIntentEligible(quick_answers::Intent intent);
  static bool IsIntentEligibleAs(quick_answers::Intent intent,
                                 FeatureType feature_type);

  QuickAnswersState();

  QuickAnswersState(const QuickAnswersState&) = delete;
  QuickAnswersState& operator=(const QuickAnswersState&) = delete;

  ~QuickAnswersState() override;

  // Observers are notified only in the context of current feature type.
  void AddObserver(QuickAnswersStateObserver* observer);
  void RemoveObserver(QuickAnswersStateObserver* observer);

  // chromeos::MagicBoostState::Observer:
  void OnMagicBoostAvailableUpdated(bool available) override;
  void OnHMREnabledUpdated(bool enabled) override;
  void OnHMRConsentStatusUpdated(
      chromeos::HMRConsentStatus consent_status) override;
  void OnIsDeleting() override;

  // Write consent status and a respective enabled state to the pref. Note that
  // this method returns BEFORE a write is completed. Reading consent status
  // and/or enabled state immediately after the write can read a stale value.
  // TODO(b/340628526): Add validations, e.g., fail to set kAccepted if it's in
  // kiosk mode, etc.
  void AsyncSetConsentStatus(
      quick_answers::prefs::ConsentStatus consent_status);

  // Increment impression count and returns an incremented count. Note that this
  // method is not thread safe, i.e., this does NOT operate an increment as an
  // atomic operation. Reading impression count immediately after the write can
  // read a stale value.
  int32_t AsyncIncrementImpressionCount();

  bool ShouldUseQuickAnswersTextAnnotator();

  bool IsSupportedLanguage(std::string_view language) const;

  const std::string& application_locale() const {
    return resolved_application_locale_;
  }
  const std::string& preferred_languages() const {
    return preferred_languages_;
  }
  bool spoken_feedback_enabled() const { return spoken_feedback_enabled_; }
  bool prefs_initialized() const { return prefs_initialized_; }

  void SetEligibilityForTesting(bool is_eligible);
  void set_use_text_annotator_for_testing() {
    use_text_annotator_for_testing_ = true;
  }

 protected:
  // All AsyncWrite.+ functions return BEFORE a write is completed, i.e., write
  // can be an async operation. Immediately reading a respective value might end
  // up a stale value.
  virtual void AsyncWriteConsentUiImpressionCount(int32_t count) = 0;
  virtual void AsyncWriteConsentStatus(
      quick_answers::prefs::ConsentStatus consent_status) = 0;
  virtual void AsyncWriteEnabled(bool enabled) = 0;

  // `FakeQuickAnswersState` overrides this method to fake feature type.
  virtual base::expected<FeatureType, Error> GetFeatureTypeExpected() const;

  // Set consent status of Quick Answers capability as a Quick Answers feature.
  void SetQuickAnswersFeatureConsentStatus(
      quick_answers::prefs::ConsentStatus consent_status);

  void SetIntentEligibilityAsQuickAnswers(quick_answers::Intent intent,
                                          bool eligible);

  void InitializeObserver(QuickAnswersStateObserver* observer);

  // Notify eligibility change to observers in the current feature type if it
  // has changed.
  void MaybeNotifyEligibilityChanged();
  void MaybeNotifyIsEnabledChanged();

  // Record the consent result with how many times the user has seen the consent
  // and impression duration.
  void RecordConsentResult(ConsentResultType type,
                           int nth_impression,
                           const base::TimeDelta duration);



  // The resolved application locale.
  std::string resolved_application_locale_;

  // The list of preferred languages, separated by comma.
  // (ex. "en-US,zh,fr").
  std::string preferred_languages_;

  // Whether the a11y spoken feedback tool is enabled.
  bool spoken_feedback_enabled_;

  // The number of times a user has seen the consent.
  int32_t consent_ui_impression_count_ = 0;

  // Whether the pref values has been initialized.
  bool prefs_initialized_ = false;

  // Whether to use text annotator for testing.
  bool use_text_annotator_for_testing_ = false;

  // Whether the Quick Answers is enabled in system settings.
  base::expected<bool, Error> quick_answers_enabled_ =
      base::unexpected(Error::kUninitialized);

  base::ObserverList<QuickAnswersStateObserver> observers_;

 private:
  void MaybeNotifyFeatureTypeChanged();
  void MaybeNotifyConsentStatusChanged();

  // Holds consent status of Quick Answers capability as a Quick Answers
  // feature.
  base::expected<quick_answers::prefs::ConsentStatus, Error>
      quick_answers_consent_status_ = base::unexpected(Error::kUninitialized);

  // Whether the definition is eligible as Quick Answers feature.
  base::expected<bool, Error> quick_answers_definition_eligible_ =
      base::unexpected(Error::kUninitialized);

  // Whether the translation is eligible as Quick Answers feature.
  base::expected<bool, Error> quick_answers_translation_eligible_ =
      base::unexpected(Error::kUninitialized);

  // Whether the unit conversion is eligible as Quick Answers feature.
  base::expected<bool, Error> quick_answers_unit_conversion_eligible_ =
      base::unexpected(Error::kUninitialized);

  // Use `base::expected` instead of `std::optional` to avoid implicit bool
  // conversion: https://abseil.io/tips/141.
  //
  // Dependencies:
  // - IsEligible <- ApplicationLocale
  // - IsEnabled <- IsEligible, GetConsentStatus
  // - GetConsentStatus <- none
  // - IsIntentEligible <- IsEligible
  //
  // Remember to call dependent values notify method if a value has changed,
  // e.g., call `MaybeNotifyIsEnabled` from `MaybeNotifyGetConsentStatus`.
  base::expected<bool, Error> IsEligibleExpected() const;
  base::expected<bool, Error> IsEligibleExpectedAs(
      FeatureType feature_type) const;
  base::expected<bool, Error> IsEnabledExpected() const;
  base::expected<bool, Error> IsEnabledExpectedAs(
      FeatureType feature_type) const;
  base::expected<quick_answers::prefs::ConsentStatus, Error>
  GetConsentStatusExpected() const;
  base::expected<quick_answers::prefs::ConsentStatus, Error>
  GetConsentStatusExpectedAs(FeatureType feature_type) const;
  base::expected<bool, Error> IsIntentEligibleExpected(
      quick_answers::Intent intent) const;
  base::expected<bool, Error> IsIntentEligibleExpectedAs(
      quick_answers::Intent intent,
      FeatureType feature_type) const;

  // Last notified values in the current feature type.
  base::expected<QuickAnswersState::FeatureType, Error>
      last_notified_feature_type_ = base::unexpected(Error::kUninitialized);
  base::expected<bool, Error> last_notified_is_eligible_ =
      base::unexpected(Error::kUninitialized);
  base::expected<bool, Error> last_notified_is_enabled_ =
      base::unexpected(Error::kUninitialized);
  base::expected<quick_answers::prefs::ConsentStatus, Error>
      last_notified_consent_status_ = base::unexpected(Error::kUninitialized);

  base::ScopedObservation<chromeos::MagicBoostState,
                          chromeos::MagicBoostState::Observer>
      magic_boost_state_observation_{this};

  // Test overwrite values.
  std::optional<bool> is_eligible_for_testing_;
};

#endif  // CHROMEOS_COMPONENTS_QUICK_ANSWERS_PUBLIC_CPP_QUICK_ANSWERS_STATE_H_