123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- // Copyright 2021 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "components/user_education/common/tutorial_service.h"
- #include <memory>
- #include <vector>
- #include "base/auto_reset.h"
- #include "components/user_education/common/help_bubble.h"
- #include "components/user_education/common/help_bubble_factory_registry.h"
- #include "components/user_education/common/tutorial.h"
- #include "components/user_education/common/tutorial_identifier.h"
- #include "components/user_education/common/tutorial_registry.h"
- namespace user_education {
- TutorialService::TutorialCreationParams::TutorialCreationParams(
- TutorialDescription* description,
- ui::ElementContext context)
- : description_(description), context_(context) {}
- TutorialService::TutorialService(
- TutorialRegistry* tutorial_registry,
- HelpBubbleFactoryRegistry* help_bubble_factory_registry)
- : tutorial_registry_(tutorial_registry),
- help_bubble_factory_registry_(help_bubble_factory_registry) {
- toggle_focus_subscription_ =
- help_bubble_factory_registry->AddToggleFocusCallback(
- base::BindRepeating(&TutorialService::OnFocusToggledForAccessibility,
- base::Unretained(this)));
- }
- TutorialService::~TutorialService() = default;
- bool TutorialService::StartTutorial(TutorialIdentifier id,
- ui::ElementContext context,
- CompletedCallback completed_callback,
- AbortedCallback aborted_callback) {
- // Overriding an existing running tutorial is not supported. In this case
- // return false to the caller.
- if (running_tutorial_)
- return false;
- // Get the description from the tutorial registry.
- TutorialDescription* description =
- tutorial_registry_->GetTutorialDescription(id);
- CHECK(description);
- // Construct the tutorial from the dsecription.
- running_tutorial_ =
- Tutorial::Builder::BuildFromDescription(*description, this, context);
- // Set the external callbacks.
- completed_callback_ = std::move(completed_callback);
- aborted_callback_ = std::move(aborted_callback);
- // Save the params for creating the tutorial to be used when restarting.
- running_tutorial_creation_params_ =
- std::make_unique<TutorialCreationParams>(description, context);
- // Start the tutorial and mark the params used to created it for restarting.
- running_tutorial_->Start();
- toggle_focus_count_ = 0;
- return true;
- }
- void TutorialService::LogIPHLinkClicked(TutorialIdentifier id,
- bool iph_link_was_clicked) {
- TutorialDescription* description =
- tutorial_registry_->GetTutorialDescription(id);
- CHECK(description);
- if (description->histograms)
- description->histograms->RecordIphLinkClicked(iph_link_was_clicked);
- }
- void TutorialService::LogStartedFromWhatsNewPage(TutorialIdentifier id,
- bool success) {
- TutorialDescription* description =
- tutorial_registry_->GetTutorialDescription(id);
- CHECK(description);
- if (description->histograms)
- description->histograms->RecordStartedFromWhatsNewPage(success);
- }
- bool TutorialService::RestartTutorial() {
- DCHECK(running_tutorial_ && running_tutorial_creation_params_);
- base::AutoReset<bool> resetter(&is_restarting_, true);
- currently_displayed_bubble_.reset();
- running_tutorial_ = Tutorial::Builder::BuildFromDescription(
- *running_tutorial_creation_params_->description_, this,
- running_tutorial_creation_params_->context_);
- if (!running_tutorial_) {
- ResetRunningTutorial();
- return false;
- }
- // Note: if we restart the tutorial, we won't record whether the user pressed
- // the pane focus key to focus the help bubble until the user actually decides
- // they're finished, but we also won't reset the count, so at the end we can
- // record the total.
- // TODO(dfried): decide if this is actually the correct behavior.
- running_tutorial_was_restarted_ = true;
- running_tutorial_->Start();
- return true;
- }
- void TutorialService::AbortTutorial(absl::optional<int> abort_step) {
- // For various reasons, we could get called here while e.g. tearing down the
- // interaction sequence. We only want to actually run AbortTutorial() or
- // CompleteTutorial() exactly once, so we won't continue if the tutorial has
- // already been disposed. We also only want to abort the tutorial if we are
- // not in the process of restarting. When calling reset on the help bubble,
- // or when resetting the tutorial, the interaction sequence or callbacks could
- // call the abort.
- if (!running_tutorial_ || is_restarting_)
- return;
- // If the tutorial had been restarted and then aborted, The tutorial should be
- // considered completed.
- if (running_tutorial_was_restarted_)
- return CompleteTutorial();
- if (abort_step.has_value())
- running_tutorial_creation_params_->description_->histograms
- ->RecordAbortStep(abort_step.value());
- // Log the failure of completion for the tutorial.
- if (running_tutorial_creation_params_->description_->histograms)
- running_tutorial_creation_params_->description_->histograms->RecordComplete(
- false);
- UMA_HISTOGRAM_BOOLEAN("Tutorial.Completion", false);
- // Reset the tutorial and call the external abort callback.
- ResetRunningTutorial();
- // Record how many times the user toggled focus during the tutorial using
- // the keyboard.
- UMA_HISTOGRAM_CUSTOM_COUNTS("Tutorial.FocusToggleCount.Aborted",
- toggle_focus_count_, 0, 50, 6);
- toggle_focus_count_ = 0;
- if (aborted_callback_) {
- std::move(aborted_callback_).Run();
- }
- }
- void TutorialService::CompleteTutorial() {
- DCHECK(running_tutorial_);
- // Log the completion metric based on if the tutorial was restarted or not.
- if (running_tutorial_creation_params_->description_->histograms)
- running_tutorial_creation_params_->description_->histograms->RecordComplete(
- true);
- UMA_HISTOGRAM_BOOLEAN("Tutorial.Completion", true);
- ResetRunningTutorial();
- // Record how many times the user toggled focus during the tutorial using
- // the keyboard.
- UMA_HISTOGRAM_CUSTOM_COUNTS("Tutorial.FocusToggleCount.Completed",
- toggle_focus_count_, 0, 50, 6);
- toggle_focus_count_ = 0;
- std::move(completed_callback_).Run();
- }
- void TutorialService::SetCurrentBubble(std::unique_ptr<HelpBubble> bubble) {
- DCHECK(running_tutorial_);
- currently_displayed_bubble_ = std::move(bubble);
- }
- void TutorialService::HideCurrentBubbleIfShowing() {
- if (currently_displayed_bubble_) {
- currently_displayed_bubble_.reset();
- }
- }
- bool TutorialService::IsRunningTutorial() const {
- return running_tutorial_ != nullptr;
- }
- void TutorialService::ResetRunningTutorial() {
- DCHECK(running_tutorial_);
- running_tutorial_.reset();
- running_tutorial_creation_params_.reset();
- running_tutorial_was_restarted_ = false;
- currently_displayed_bubble_.reset();
- }
- void TutorialService::OnFocusToggledForAccessibility(HelpBubble* bubble) {
- if (bubble == currently_displayed_bubble_.get())
- ++toggle_focus_count_;
- }
- } // namespace user_education
|