tracker.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #ifndef COMPONENTS_FEATURE_ENGAGEMENT_PUBLIC_TRACKER_H_
  5. #define COMPONENTS_FEATURE_ENGAGEMENT_PUBLIC_TRACKER_H_
  6. #include <memory>
  7. #include <string>
  8. #include "base/callback.h"
  9. #include "base/feature_list.h"
  10. #include "base/files/file_path.h"
  11. #include "base/memory/ref_counted.h"
  12. #include "base/supports_user_data.h"
  13. #include "base/task/sequenced_task_runner.h"
  14. #include "build/build_config.h"
  15. #include "components/keyed_service/core/keyed_service.h"
  16. #include "third_party/abseil-cpp/absl/types/optional.h"
  17. #if BUILDFLAG(IS_ANDROID)
  18. #include "base/android/jni_android.h"
  19. #endif // BUILDFLAG(IS_ANDROID)
  20. namespace leveldb_proto {
  21. class ProtoDatabaseProvider;
  22. }
  23. namespace feature_engagement {
  24. // A handle for the display lock. While this is unreleased, no in-product help
  25. // can be displayed.
  26. class DisplayLockHandle {
  27. public:
  28. typedef base::OnceClosure ReleaseCallback;
  29. explicit DisplayLockHandle(ReleaseCallback callback);
  30. DisplayLockHandle(const DisplayLockHandle&) = delete;
  31. DisplayLockHandle& operator=(const DisplayLockHandle&) = delete;
  32. ~DisplayLockHandle();
  33. private:
  34. ReleaseCallback release_callback_;
  35. };
  36. // The Tracker provides a backend for displaying feature
  37. // enlightenment or in-product help (IPH) with a clean and easy to use API to be
  38. // consumed by the UI frontend. The backend behaves as a black box and takes
  39. // input about user behavior. Whenever the frontend gives a trigger signal that
  40. // IPH could be displayed, the backend will provide an answer to whether it is
  41. // appropriate to show it or not.
  42. class Tracker : public KeyedService, public base::SupportsUserData {
  43. public:
  44. // Describes the state of whether in-product helps has already been displayed
  45. // enough times or not within the bounds of the configuration for a
  46. // base::Feature. NOT_READY is returned if the Tracker has not been
  47. // initialized yet before the call to GetTriggerState(...).
  48. // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.feature_engagement
  49. enum class TriggerState : int {
  50. HAS_BEEN_DISPLAYED = 0,
  51. HAS_NOT_BEEN_DISPLAYED = 1,
  52. NOT_READY = 2
  53. };
  54. // Represents the action taken by the user on the snooze UI.
  55. // These enums are persisted as histogram entries, so this enum should be
  56. // treated as append-only and kept in sync with InProductHelpSnoozeAction in
  57. // enums.xml.
  58. // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.feature_engagement
  59. enum class SnoozeAction : int {
  60. // User chose to snooze the IPH.
  61. SNOOZED = 1,
  62. // User chose to dismiss the IPH.
  63. DISMISSED = 2,
  64. // Constant used by the histogram macros.
  65. kMaxValue = DISMISSED
  66. };
  67. // Result of the backend query for whether or not to trigger any help UI.
  68. // A similar class will also be added to the java layer.
  69. struct TriggerDetails {
  70. public:
  71. TriggerDetails(bool should_trigger_iph, bool should_show_snooze);
  72. TriggerDetails(const TriggerDetails& trigger_details);
  73. ~TriggerDetails();
  74. // Whether or not to show the help UI.
  75. bool ShouldShowIph() const;
  76. // Whether to show a snooze option in the help UI.
  77. bool ShouldShowSnooze() const;
  78. private:
  79. bool should_trigger_iph_;
  80. bool should_show_snooze_;
  81. };
  82. #if BUILDFLAG(IS_ANDROID)
  83. // Returns a Java object of the type Tracker for the given Tracker.
  84. static base::android::ScopedJavaLocalRef<jobject> GetJavaObject(
  85. Tracker* feature_engagement);
  86. #endif // BUILDFLAG(IS_ANDROID)
  87. // Invoked when the tracker has been initialized. The |success| parameter
  88. // indicates that the initialization was a success and the tracker is ready to
  89. // receive calls.
  90. using OnInitializedCallback = base::OnceCallback<void(bool success)>;
  91. // The |storage_dir| is the path to where all local storage will be.
  92. // The |bakground_task_runner| will be used for all disk reads and writes.
  93. static Tracker* Create(
  94. const base::FilePath& storage_dir,
  95. const scoped_refptr<base::SequencedTaskRunner>& background_task_runner,
  96. leveldb_proto::ProtoDatabaseProvider* db_provider);
  97. Tracker(const Tracker&) = delete;
  98. Tracker& operator=(const Tracker&) = delete;
  99. // Must be called whenever an event happens.
  100. virtual void NotifyEvent(const std::string& event) = 0;
  101. // This function must be called whenever the triggering condition for a
  102. // specific feature happens. Returns true iff the display of the in-product
  103. // help must happen.
  104. // If |true| is returned, the caller *must* call Dismissed(...) when display
  105. // of feature enlightenment ends.
  106. [[nodiscard]] virtual bool ShouldTriggerHelpUI(
  107. const base::Feature& feature) = 0;
  108. // For callers interested in showing a snooze button. For other callers, use
  109. // the ShouldTriggerHelpUI(..) method.
  110. virtual TriggerDetails ShouldTriggerHelpUIWithSnooze(
  111. const base::Feature& feature) = 0;
  112. // Invoking this is basically the same as being allowed to invoke
  113. // ShouldTriggerHelpUI(...) without requiring to show the in-product help.
  114. // This function may be called to inspect if the current state would allow the
  115. // given |feature| to pass all its conditions and display the feature
  116. // enlightenment.
  117. //
  118. // NOTE: It is still required to invoke ShouldTriggerHelpUI(...) if feature
  119. // enlightenment should be shown.
  120. //
  121. // NOTE: It is not guaranteed that invoking ShouldTriggerHelpUI(...)
  122. // after this would yield the same result. The state might change
  123. // in-between the calls because time has passed, other events might have been
  124. // triggered, and other state might have changed.
  125. virtual bool WouldTriggerHelpUI(const base::Feature& feature) const = 0;
  126. // This function can be called to query if a particular |feature| has ever
  127. // been displayed at least once in the past. The days counted is controlled by
  128. // the EventConfig of "event_trigger". If |from_window| is set to true, the
  129. // search window size will be set to event_trigger.window; otherwise, the
  130. // window size will be event_trigger.storage.
  131. //
  132. // Calling this method requires the Tracker to already have been initialized.
  133. // See IsInitialized() and AddOnInitializedCallback(...) for how to ensure
  134. // the call to this is delayed.
  135. virtual bool HasEverTriggered(const base::Feature& feature,
  136. bool from_window) const = 0;
  137. // This function can be called to query if a particular |feature| meets its
  138. // particular precondition for triggering within the bounds of the current
  139. // feature configuration.
  140. // Calling this method requires the Tracker to already have been initialized.
  141. // See IsInitialized() and AddOnInitializedCallback(...) for how to ensure
  142. // the call to this is delayed.
  143. // This function can typically be used to ensure that expensive operations
  144. // for tracking other state related to in-product help do not happen if
  145. // in-product help has already been displayed for the given |feature|.
  146. virtual TriggerState GetTriggerState(const base::Feature& feature) const = 0;
  147. // Must be called after display of feature enlightenment finishes for a
  148. // particular |feature|.
  149. virtual void Dismissed(const base::Feature& feature) = 0;
  150. // For callers interested in showing a snooze button. For other callers, use
  151. // the Dismissed(..) method.
  152. virtual void DismissedWithSnooze(
  153. const base::Feature& feature,
  154. absl::optional<SnoozeAction> snooze_action) = 0;
  155. // Acquiring a display lock means that no in-product help can be displayed
  156. // while it is held. To release the lock, delete the handle.
  157. // If in-product help is already displayed while the display lock is
  158. // acquired, the lock is still handed out, but it will not dismiss the current
  159. // in-product help. However, no new in-product help will be shown until all
  160. // locks have been released. It is required to release the DisplayLockHandle
  161. // once the lock should no longer be held.
  162. // The DisplayLockHandle must be released on the main thread.
  163. // This method returns nullptr if no handle could be retrieved.
  164. virtual std::unique_ptr<DisplayLockHandle> AcquireDisplayLock() = 0;
  165. // Called by the client to notify the tracker that a priority notification
  166. // should be shown. If a handler has already been registered, the IPH will be
  167. // shown right away. Otherwise, the tracker will cache the priority feature
  168. // and will show the IPH whenever a handler is registered in future. All other
  169. // IPHs will be blocked until then. It isn't allowed to invoke this method
  170. // again with another notification before the existing one is processed.
  171. virtual void SetPriorityNotification(const base::Feature& feature) = 0;
  172. // Called to get if there is a pending priority notification to be shown next.
  173. virtual absl::optional<std::string> GetPendingPriorityNotification() = 0;
  174. // Called by the client to register a handler for priority notifications. This
  175. // will essentially contain the code to spin up an IPH.
  176. virtual void RegisterPriorityNotificationHandler(
  177. const base::Feature& feature,
  178. base::OnceClosure callback) = 0;
  179. // Unregister the handler. Must be called during client destruction.
  180. virtual void UnregisterPriorityNotificationHandler(
  181. const base::Feature& feature) = 0;
  182. // Returns whether the tracker has been successfully initialized. During
  183. // startup, this will be false until the internal models have been loaded at
  184. // which point it is set to true if the initialization was successful. The
  185. // state will never change from initialized to uninitialized.
  186. // Callers can invoke AddOnInitializedCallback(...) to be notified when the
  187. // result of the initialization is ready.
  188. virtual bool IsInitialized() const = 0;
  189. // For features that trigger on startup, they can register a callback to
  190. // ensure that they are informed when the tracker has finished the
  191. // initialization. If the tracker has already been initialized, the callback
  192. // will still be invoked with the result. The callback is guaranteed to be
  193. // invoked exactly one time.
  194. virtual void AddOnInitializedCallback(OnInitializedCallback callback) = 0;
  195. protected:
  196. Tracker() = default;
  197. };
  198. } // namespace feature_engagement
  199. #endif // COMPONENTS_FEATURE_ENGAGEMENT_PUBLIC_TRACKER_H_