File: scalable_iph.h

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (250 lines) | stat: -rw-r--r-- 10,877 bytes parent folder | download | duplicates (7)
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
// Copyright 2023 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_ASH_COMPONENTS_SCALABLE_IPH_SCALABLE_IPH_H_
#define CHROMEOS_ASH_COMPONENTS_SCALABLE_IPH_SCALABLE_IPH_H_

#include <optional>
#include <ostream>
#include <vector>

#include "base/component_export.h"
#include "base/containers/enum_set.h"
#include "base/feature_list.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/task/sequenced_task_runner.h"
#include "base/timer/timer.h"
#include "chromeos/ash/components/scalable_iph/logger.h"
#include "chromeos/ash/components/scalable_iph/scalable_iph_constants.h"
#include "chromeos/ash/components/scalable_iph/scalable_iph_delegate.h"
#include "components/feature_engagement/public/tracker.h"
#include "components/keyed_service/core/keyed_service.h"

namespace scalable_iph {

// `ScalableIph` provides a scalable way to deliver IPHs.
//
// - Scalable: we provide a scalable way by building this framework on top of
// the feature engagement framework. A developer can set up an IPH without
// modifying a binary. See feature engagement doc for details about its
// flexibility: //components/feature_engagement/README.md.
//
// - IPH: in-product-help.
//
// Class diagram:
// =============================================================================
//
// //chromeos/ash/components       | //chrome/browser/ash
// -----------------------------------------------------------------------------
//
// |-------------|
// |             |
// |             |                 |---------------------|              |-----|
// |             | -[TriggerIph]-> |                     | -[ShowUI]--> |     |
// |             | ---[Action]---> | ScalableIphDelegate | -[OpenUrl]-> | Ash |
// |             | <--[Observer]-- |                     | <-[Events]-- |     |
// |             |                 |---------------------|              |-----|
// |             |                                                         |
// |             |                 |---------|                             |
// | ScalableIph | <---[Action]--- | HelpApp |                             |
// |             |                 |---------|                             |
// |             |                                                        \|/
// |             |                                               |------------|
// |             | <-------------------[Action]----------------- | IphSession |
// |             |                                               |------------|
// |             |                                                         |
// |             |                                                        \|/
// |             |                          |---------------------------------|
// |             | -------[Interact]------> | //components/feature_engagement |
// |-------------|                          |---------------------------------|
//
// ScalableIph: The main component of Scalable Iph framework. This class checks
//              trigger conditions and parse Scalable Iph custom fields, e.g.
//              Custom conditions.
// ScalableIphDelegate: A delegate class for `ScalableIph` to delegate its tasks
//                      to Ash or Chrome. An implementation of
//                      `ScalableIphDelegate` will be in //chrome/browser/ash.
//                      Delegated tasks will be:
//                      - Show an IPH UI, e.g. Notification.
//                      - Observe events, e.g. Network connection.
//                      - Perform actions, e.g. Open a URL.
// IphSession: An object for managing a single IPH session. If an UI is opened
//             by `ScalableIph` (e.g. Notification, Bubble), `IphSession` is
//             passed to those code for `ScalableIph` to manage an IPH session
//             and for those UIs to perform actions. `IphSession` can interact
//             with a `feature_engagement::Tracker` directly as it holds a
//             reference to it. But it has to delegate actions to `ScalableIph`
//             as it is in //chromeos/ash/components. `ScalableIph` delegates
//             them again to `ScalableIphDelegate`.
//
class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_SCALABLE_IPH) ScalableIph
    : public KeyedService,
      public ScalableIphDelegate::Observer,
      public IphSession::Delegate {
 public:
  // List of events ScalableIph supports.
  enum class Event {
    kFiveMinTick = 0,
    kUnlocked,
    kAppListShown,
    kAppListItemActivationYouTube,
    kAppListItemActivationGoogleDocs,
    kAppListItemActivationGooglePhotosWeb,
    kOpenPersonalizationApp,
    kShelfItemActivationYouTube,
    kShelfItemActivationGoogleDocs,
    kShelfItemActivationGooglePhotosWeb,
    kShelfItemActivationGooglePhotosAndroid,
    kShelfItemActivationGooglePlay,
    kAppListItemActivationGooglePlayStore,
    kAppListItemActivationGooglePhotosAndroid,
    kPrintJobCreated,
    kGameWindowOpened,
  };

  enum SessionStateTransition {
    // The state machine expects that it advances its state to the new state. In
    // practice, this means that we update `session_state_` in `ScalableIph`
    // instance.
    kAdvanceState,
    // Observe this session state transition as unlocked event. Note that
    // unlocked event includes a session start in `ScalableIph`.
    kUnlock
  };

  using TransitionSet = base::EnumSet<SessionStateTransition,
                                      SessionStateTransition::kAdvanceState,
                                      SessionStateTransition::kUnlock>;

  // Returns true if any iph feature flag is enabled. Otherwise false.
  static bool IsAnyIphFeatureEnabled();

  // Force enable `IsAnyIphFeatureEnabled` check for testing. Note that no
  // actual iph feature flag gets enabled by this.
  static void ForceEnableIphFeatureForTesting();

  ScalableIph(feature_engagement::Tracker* tracker,
              std::unique_ptr<ScalableIphDelegate> delegate,
              std::unique_ptr<Logger> logger);

  void RecordEvent(Event event);

  Logger* GetLogger();

  ScalableIphDelegate* delegate_for_testing() { return delegate_.get(); }

  // KeyedService:
  ~ScalableIph() override;
  void Shutdown() override;

  // ScalableIphDelegate::Observer:
  void OnConnectionChanged(bool online) override;
  void OnSessionStateChanged(ScalableIphDelegate::SessionState state) override;
  void OnSuspendDoneWithoutLockScreen() override;
  void OnAppListVisibilityChanged(bool shown) override;
  void OnHasSavedPrintersChanged(bool has_saved_printers) override;
  void OnPhoneHubOnboardingEligibleChanged(
      bool phonehub_onboarding_eligible) override;

  // IphSession::Delegate:
  void PerformActionForIphSession(ActionType action_type) override;

  void OverrideFeatureListForTesting(
      const std::vector<raw_ptr<const base::Feature, VectorExperimental>>
          features);
  void OverrideTaskRunnerForTesting(
      scoped_refptr<base::SequencedTaskRunner> task_runner);

  // Called for a user action in the help app. All the logging related to
  // help app action events will be done here before calling `PerformAction`.
  void PerformActionForHelpApp(ActionType action_type);

  // Perform `action_type` as a result of a user action, e.g. A link click in a
  // help app, etc. This notifies a corresponding IPH event to the feature
  // engagement framework.
  //
  // UIs which were initiated with `IphSession` (e.g. Notification, Bubble)
  // should use `IphSession::PerformAction` instead of this method.
  void PerformAction(ActionType action_type);

  // `SyncedPrintersManager` stores its observers in `ObserverListThreadSafe`,
  // which invokes observers via `TaskRunner`. Test code can set a closure to
  // this method to wait an observer of `ScalableIph` being called.
  //
  // Note:
  // We cannot wait this by registering another observer in a test and wait it.
  // Observers are stored in an unordered map. There is no guarantee on the
  // order of calls.
  void SetHasSavedPrintersChangedClosureForTesting(
      base::RepeatingClosure has_saved_printers_closure);

  // Maybe record an app list item or a shelf item activation of `id`.
  void MaybeRecordAppListItemActivation(const std::string& id);
  void MaybeRecordShelfItemActivationById(const std::string& id);

  // Returns true if the help app should be pinned to the bottom shelf.
  bool ShouldPinHelpAppToShelf();

  static const std::vector<raw_ptr<const base::Feature, VectorExperimental>>&
  GetFeatureListConstantForTesting();

  static TransitionSet GetTransitionForTesting(
      ScalableIphDelegate::SessionState from,
      ScalableIphDelegate::SessionState to);

  bool CheckTriggerEventForTesting(
      const base::Feature& feature,
      const std::optional<ScalableIph::Event>& trigger_event);

 private:
  void EnsureTimerStarted();
  void RecordTimeTickEvent();
  void RecordUnlockedEvent();
  void RecordEventInternal(Event event, bool init_success);
  void CheckTriggerConditionsOnInitSuccess(bool init_success);
  void CheckTriggerConditions(
      const std::optional<ScalableIph::Event>& trigger_event);

  // Check all custom conditions assigned to `feature`. Returns true if all
  // conditions are valid and satisfied. Otherwise false including an invalid
  // config case.
  bool CheckCustomConditions(const base::Feature& feature,
                             const std::optional<Event>& trigger_event);
  bool CheckTriggerEvent(const base::Feature& feature,
                         const std::optional<Event>& trigger_event);
  bool CheckNetworkConnection(const base::Feature& feature);
  bool CheckClientAge(const base::Feature& feature);
  bool CheckHasSavedPrinters(const base::Feature& feature);
  bool CheckPhoneHubOnboardingEligible(const base::Feature& feature);

  const std::vector<raw_ptr<const base::Feature, VectorExperimental>>&
  GetFeatureList() const;

  raw_ptr<feature_engagement::Tracker> tracker_;
  std::unique_ptr<ScalableIphDelegate> delegate_;
  base::RepeatingTimer timer_;
  bool online_ = false;
  ScalableIphDelegate::SessionState session_state_ =
      ScalableIphDelegate::SessionState::kUnknownInitialValue;
  bool has_saved_printers_ = false;
  bool phonehub_onboarding_eligible_ = false;
  std::unique_ptr<Logger> logger_;

  base::RepeatingClosure has_saved_printers_closure_for_testing_;
  std::vector<raw_ptr<const base::Feature, VectorExperimental>>
      feature_list_for_testing_;

  base::ScopedObservation<ScalableIphDelegate, ScalableIph>
      delegate_observation_{this};

  base::WeakPtrFactory<ScalableIph> weak_ptr_factory_{this};
};

std::ostream& operator<<(std::ostream& out, ScalableIph::Event event);

}  // namespace scalable_iph

#endif  // CHROMEOS_ASH_COMPONENTS_SCALABLE_IPH_SCALABLE_IPH_H_