blob: bbecc1aa54bf4c85fd9c9144eb60ce91eeb9df0a [file] [log] [blame]
// 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.
#ifndef COMPONENTS_USER_EDUCATION_COMMON_TUTORIAL_DESCRIPTION_H_
#define COMPONENTS_USER_EDUCATION_COMMON_TUTORIAL_DESCRIPTION_H_
#include <string>
#include <vector>
#include "base/metrics/histogram_macros.h"
#include "components/user_education/common/help_bubble_params.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/base/interaction/element_tracker.h"
#include "ui/base/interaction/interaction_sequence.h"
namespace user_education {
// Holds the data required to properly store histograms for a given tutorial.
// Abstract base class because best practice is to statically declare
// histograms and so we need some compile-time polymorphism to actually
// implement the RecordXXX() calls.
//
// Use MakeTutorialHistograms() below to create a concrete instance of this
// class.
class TutorialHistograms {
public:
TutorialHistograms() = default;
TutorialHistograms(const TutorialHistograms& other) = delete;
virtual ~TutorialHistograms() = default;
void operator=(const TutorialHistograms& other) = delete;
// Records whether the tutorial was completed or not.
virtual void RecordComplete(bool value) = 0;
// Records the step on which the tutorial was aborted.
virtual void RecordAbortStep(int step) = 0;
// Records whether, when an IPH offered the tutorial, the user opted into
// seeing the tutorial or not.
virtual void RecordIphLinkClicked(bool value) = 0;
};
namespace internal {
constexpr char kTutorialHistogramPrefix[] = "Tutorial.";
template <const char histogram_name[]>
class TutorialHistogramsImpl : public TutorialHistograms {
public:
explicit TutorialHistogramsImpl(int max_steps)
: histogram_name_(histogram_name),
completed_name_(kTutorialHistogramPrefix + histogram_name_ +
".Completion"),
aborted_name_(kTutorialHistogramPrefix + histogram_name_ +
".AbortStep"),
link_clicked_name_(kTutorialHistogramPrefix + histogram_name_ +
".IPHLinkClicked"),
max_steps_(max_steps) {}
~TutorialHistogramsImpl() override = default;
protected:
void RecordComplete(bool value) override {
UMA_HISTOGRAM_BOOLEAN(completed_name_, value);
}
void RecordAbortStep(int step) override {
UMA_HISTOGRAM_EXACT_LINEAR(aborted_name_, step, max_steps_);
}
void RecordIphLinkClicked(bool value) override {
UMA_HISTOGRAM_BOOLEAN(link_clicked_name_, value);
}
private:
const std::string histogram_name_;
const std::string completed_name_;
const std::string aborted_name_;
const std::string link_clicked_name_;
const int max_steps_;
};
} // namespace internal
// Call to create a tutorial-specific histograms object for use with the
// tutorial. The template parameter should be a reference to a const char[]
// that is a compile-time constant. Also remember to add a matching entry to
// the "TutorialID" variant in histograms.xml corresponding to your tutorial.
//
// Example:
// const char kMyTutorialName[] = "MyTutorial";
// tutorial_descriptions.histograms =
// MakeTutorialHistograms<kMyTutorialName>(
// tutorial_description.steps.size());
template <const char* histogram_name>
std::unique_ptr<TutorialHistograms> MakeTutorialHistograms(int max_steps) {
return std::make_unique<internal::TutorialHistogramsImpl<histogram_name>>(
max_steps);
}
// A Struct that provides all of the data necessary to construct a Tutorial.
// A Tutorial Description is a list of Steps for a tutorial. Each step has info
// for constructing the InteractionSequence::Step from the
// TutorialDescription::Step.
struct TutorialDescription {
using NameElementsCallback =
base::RepeatingCallback<bool(ui::InteractionSequence*,
ui::TrackedElement*)>;
TutorialDescription();
~TutorialDescription();
TutorialDescription(TutorialDescription&& other);
TutorialDescription& operator=(TutorialDescription&& other);
struct Step {
Step();
Step(int title_text_id_,
int body_text_id_,
ui::InteractionSequence::StepType step_type_,
ui::ElementIdentifier element_id_,
std::string element_name_,
HelpBubbleArrow arrow_,
ui::CustomElementEventType event_type_ = ui::CustomElementEventType(),
absl::optional<bool> must_remain_visible_ = absl::nullopt,
bool transition_only_on_event_ = false,
NameElementsCallback name_elements_callback_ = NameElementsCallback());
Step(const Step& other);
Step& operator=(const Step& other);
~Step();
// The title text to be populated in the bubble.
int title_text_id = 0;
// The body text to be populated in the bubble.
int body_text_id = 0;
// The step type for InteractionSequence::Step.
ui::InteractionSequence::StepType step_type;
// The event type for the step if `step_type` is kCustomEvent.
ui::CustomElementEventType event_type;
// The element used by interaction sequence to observe and attach a bubble.
ui::ElementIdentifier element_id;
// The element, referred to by name, used by the interaction sequence
// to observe and potentially attach a bubble. must be non-empty.
std::string element_name;
// The positioning of the bubble arrow.
HelpBubbleArrow arrow = HelpBubbleArrow::kTopRight;
// Should the element remain visible through the entire step, this should be
// set to false for hidden steps and for shown steps that precede hidden
// steps on the same element. if left empty the interaction sequence will
// decide what its value should be based on the generated
// InteractionSequence::StepBuilder
absl::optional<bool> must_remain_visible;
// Should the step only be completed when an event like shown or hidden only
// happens during current step. for more information on the implementation
// take a look at transition_only_on_event in InteractionSequence::Step
bool transition_only_on_event = false;
// lambda which is called on the start callback of the InteractionSequence
// which provides the interaction sequence and the current element that
// belongs to the step. The intention for this functionality is to name one
// or many elements using the Framework's Specific API finding an element
// and naming it OR using the current element from the sequence as the
// element for naming. The return value is a boolean which controls whether
// the Interaction Sequence should continue or not. If false is returned
// the tutorial will abort
NameElementsCallback name_elements_callback;
// returns true iff all of the required parameters exist to display a
// bubble.
bool ShouldShowBubble() const;
};
// the list of TutorialDescription steps
std::vector<Step> steps;
// The histogram data to use. Use MakeTutorialHistograms() above to create a
// value to use, if you want to record specific histograms for this tutorial.
std::unique_ptr<TutorialHistograms> histograms;
// The ability for the tutorial to be restarted. In some cases tutorials can
// leave the UI in a state where it can not re-run the tutorial. In these
// cases this flag should be set to false so that the restart tutorial button
// is not displayed.
bool can_be_restarted = false;
};
} // namespace user_education
#endif // COMPONENTS_USER_EDUCATION_COMMON_TUTORIAL_DESCRIPTION_H_