tutorial_service.cc 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. // Copyright 2021 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. #include "components/user_education/common/tutorial_service.h"
  5. #include <memory>
  6. #include <vector>
  7. #include "base/auto_reset.h"
  8. #include "components/user_education/common/help_bubble.h"
  9. #include "components/user_education/common/help_bubble_factory_registry.h"
  10. #include "components/user_education/common/tutorial.h"
  11. #include "components/user_education/common/tutorial_identifier.h"
  12. #include "components/user_education/common/tutorial_registry.h"
  13. namespace user_education {
  14. TutorialService::TutorialCreationParams::TutorialCreationParams(
  15. TutorialDescription* description,
  16. ui::ElementContext context)
  17. : description_(description), context_(context) {}
  18. TutorialService::TutorialService(
  19. TutorialRegistry* tutorial_registry,
  20. HelpBubbleFactoryRegistry* help_bubble_factory_registry)
  21. : tutorial_registry_(tutorial_registry),
  22. help_bubble_factory_registry_(help_bubble_factory_registry) {
  23. toggle_focus_subscription_ =
  24. help_bubble_factory_registry->AddToggleFocusCallback(
  25. base::BindRepeating(&TutorialService::OnFocusToggledForAccessibility,
  26. base::Unretained(this)));
  27. }
  28. TutorialService::~TutorialService() = default;
  29. bool TutorialService::StartTutorial(TutorialIdentifier id,
  30. ui::ElementContext context,
  31. CompletedCallback completed_callback,
  32. AbortedCallback aborted_callback) {
  33. // Overriding an existing running tutorial is not supported. In this case
  34. // return false to the caller.
  35. if (running_tutorial_)
  36. return false;
  37. // Get the description from the tutorial registry.
  38. TutorialDescription* description =
  39. tutorial_registry_->GetTutorialDescription(id);
  40. CHECK(description);
  41. // Construct the tutorial from the dsecription.
  42. running_tutorial_ =
  43. Tutorial::Builder::BuildFromDescription(*description, this, context);
  44. // Set the external callbacks.
  45. completed_callback_ = std::move(completed_callback);
  46. aborted_callback_ = std::move(aborted_callback);
  47. // Save the params for creating the tutorial to be used when restarting.
  48. running_tutorial_creation_params_ =
  49. std::make_unique<TutorialCreationParams>(description, context);
  50. // Start the tutorial and mark the params used to created it for restarting.
  51. running_tutorial_->Start();
  52. toggle_focus_count_ = 0;
  53. return true;
  54. }
  55. void TutorialService::LogIPHLinkClicked(TutorialIdentifier id,
  56. bool iph_link_was_clicked) {
  57. TutorialDescription* description =
  58. tutorial_registry_->GetTutorialDescription(id);
  59. CHECK(description);
  60. if (description->histograms)
  61. description->histograms->RecordIphLinkClicked(iph_link_was_clicked);
  62. }
  63. void TutorialService::LogStartedFromWhatsNewPage(TutorialIdentifier id,
  64. bool success) {
  65. TutorialDescription* description =
  66. tutorial_registry_->GetTutorialDescription(id);
  67. CHECK(description);
  68. if (description->histograms)
  69. description->histograms->RecordStartedFromWhatsNewPage(success);
  70. }
  71. bool TutorialService::RestartTutorial() {
  72. DCHECK(running_tutorial_ && running_tutorial_creation_params_);
  73. base::AutoReset<bool> resetter(&is_restarting_, true);
  74. currently_displayed_bubble_.reset();
  75. running_tutorial_ = Tutorial::Builder::BuildFromDescription(
  76. *running_tutorial_creation_params_->description_, this,
  77. running_tutorial_creation_params_->context_);
  78. if (!running_tutorial_) {
  79. ResetRunningTutorial();
  80. return false;
  81. }
  82. // Note: if we restart the tutorial, we won't record whether the user pressed
  83. // the pane focus key to focus the help bubble until the user actually decides
  84. // they're finished, but we also won't reset the count, so at the end we can
  85. // record the total.
  86. // TODO(dfried): decide if this is actually the correct behavior.
  87. running_tutorial_was_restarted_ = true;
  88. running_tutorial_->Start();
  89. return true;
  90. }
  91. void TutorialService::AbortTutorial(absl::optional<int> abort_step) {
  92. // For various reasons, we could get called here while e.g. tearing down the
  93. // interaction sequence. We only want to actually run AbortTutorial() or
  94. // CompleteTutorial() exactly once, so we won't continue if the tutorial has
  95. // already been disposed. We also only want to abort the tutorial if we are
  96. // not in the process of restarting. When calling reset on the help bubble,
  97. // or when resetting the tutorial, the interaction sequence or callbacks could
  98. // call the abort.
  99. if (!running_tutorial_ || is_restarting_)
  100. return;
  101. // If the tutorial had been restarted and then aborted, The tutorial should be
  102. // considered completed.
  103. if (running_tutorial_was_restarted_)
  104. return CompleteTutorial();
  105. if (abort_step.has_value())
  106. running_tutorial_creation_params_->description_->histograms
  107. ->RecordAbortStep(abort_step.value());
  108. // Log the failure of completion for the tutorial.
  109. if (running_tutorial_creation_params_->description_->histograms)
  110. running_tutorial_creation_params_->description_->histograms->RecordComplete(
  111. false);
  112. UMA_HISTOGRAM_BOOLEAN("Tutorial.Completion", false);
  113. // Reset the tutorial and call the external abort callback.
  114. ResetRunningTutorial();
  115. // Record how many times the user toggled focus during the tutorial using
  116. // the keyboard.
  117. UMA_HISTOGRAM_CUSTOM_COUNTS("Tutorial.FocusToggleCount.Aborted",
  118. toggle_focus_count_, 0, 50, 6);
  119. toggle_focus_count_ = 0;
  120. if (aborted_callback_) {
  121. std::move(aborted_callback_).Run();
  122. }
  123. }
  124. void TutorialService::CompleteTutorial() {
  125. DCHECK(running_tutorial_);
  126. // Log the completion metric based on if the tutorial was restarted or not.
  127. if (running_tutorial_creation_params_->description_->histograms)
  128. running_tutorial_creation_params_->description_->histograms->RecordComplete(
  129. true);
  130. UMA_HISTOGRAM_BOOLEAN("Tutorial.Completion", true);
  131. ResetRunningTutorial();
  132. // Record how many times the user toggled focus during the tutorial using
  133. // the keyboard.
  134. UMA_HISTOGRAM_CUSTOM_COUNTS("Tutorial.FocusToggleCount.Completed",
  135. toggle_focus_count_, 0, 50, 6);
  136. toggle_focus_count_ = 0;
  137. std::move(completed_callback_).Run();
  138. }
  139. void TutorialService::SetCurrentBubble(std::unique_ptr<HelpBubble> bubble) {
  140. DCHECK(running_tutorial_);
  141. currently_displayed_bubble_ = std::move(bubble);
  142. }
  143. void TutorialService::HideCurrentBubbleIfShowing() {
  144. if (currently_displayed_bubble_) {
  145. currently_displayed_bubble_.reset();
  146. }
  147. }
  148. bool TutorialService::IsRunningTutorial() const {
  149. return running_tutorial_ != nullptr;
  150. }
  151. void TutorialService::ResetRunningTutorial() {
  152. DCHECK(running_tutorial_);
  153. running_tutorial_.reset();
  154. running_tutorial_creation_params_.reset();
  155. running_tutorial_was_restarted_ = false;
  156. currently_displayed_bubble_.reset();
  157. }
  158. void TutorialService::OnFocusToggledForAccessibility(HelpBubble* bubble) {
  159. if (bubble == currently_displayed_bubble_.get())
  160. ++toggle_focus_count_;
  161. }
  162. } // namespace user_education