// Copyright 2019 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_COMPONENTS_QUICK_ANSWERS_QUICK_ANSWERS_MODEL_H_
#define CHROMEOS_COMPONENTS_QUICK_ANSWERS_QUICK_ANSWERS_MODEL_H_
#include <string>
#include <vector>
#include "base/strings/utf_string_conversions.h"
#include "chromeos/components/quick_answers/public/cpp/constants.h"
#include "chromeos/components/quick_answers/utils/unit_conversion_constants.h"
#include "ui/color/color_id.h"
#include "ui/gfx/image/image.h"
#include "url/gurl.h"
namespace quick_answers {
// Interaction with the consent-view (used for logging).
enum class NoticeInteractionType {
// When user clicks on the "grant-consent" button.
kAccept = 0,
// When user clicks on the "manage-settings" button.
kManageSettings = 1,
// When user otherwise dismisses or ignores the consent-view.
kDismiss = 2
};
// The status of loading quick answers.
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
// Note: Enums labels are at |QuickAnswersLoadStatus|.
enum class LoadStatus {
kSuccess = 0,
kNetworkError = 1,
kNoResult = 2,
kMaxValue = kNoResult,
};
// The type of the result. Valid values are map to the search result types.
// Please see go/1ns-doc for more detail.
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
// Note: Enums labels are at |QuickAnswersResultType|.
enum class ResultType {
kNoResult = 0,
kDefinitionResult = 5493,
kTranslationResult = 6613,
kUnitConversionResult = 13668,
};
// The predicted intent of the request.
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
// Note: Enums labels are at |QuickAnswersIntentType|.
enum class IntentType {
kUnknown = 0,
kUnit = 1,
kDictionary = 2,
kTranslation = 3,
kMaxValue = kTranslation
};
std::optional<quick_answers::Intent> ToIntent(IntentType intent_type);
enum class QuickAnswerUiElementType {
kUnknown = 0,
kText = 1,
kImage = 2,
};
// Enumeration of Quick Answers exit points. These values are persisted to logs.
// Entries should never be renumbered and numeric values should never be reused.
// Append to this enum is allowed only if the possible exit point grows.
enum class QuickAnswersExitPoint {
// The exit point is unspecified. Might be used by tests, obsolete code or as
// placeholders.
kUnspecified = 0,
kContextMenuDismiss = 1,
kContextMenuClick = 2,
kQuickAnswersClick = 3,
kSettingsButtonClick = 4,
kReportQueryButtonClick = 5,
kMaxValue = kReportQueryButtonClick,
};
struct QuickAnswerUiElement {
explicit QuickAnswerUiElement(QuickAnswerUiElementType type) : type(type) {}
QuickAnswerUiElement(const QuickAnswerUiElement&) = default;
QuickAnswerUiElement& operator=(const QuickAnswerUiElement&) = default;
QuickAnswerUiElement(QuickAnswerUiElement&&) = default;
virtual ~QuickAnswerUiElement() = default;
QuickAnswerUiElementType type = QuickAnswerUiElementType::kUnknown;
};
// Class to describe an answer text.
struct QuickAnswerText : public QuickAnswerUiElement {
explicit QuickAnswerText(const std::string& text,
ui::ColorId color_id = ui::kColorLabelForeground)
: QuickAnswerUiElement(QuickAnswerUiElementType::kText),
text(base::UTF8ToUTF16(text)),
color_id(color_id) {}
std::u16string text;
// Attributes for text style.
ui::ColorId color_id;
};
struct QuickAnswerResultText : public QuickAnswerText {
public:
QuickAnswerResultText(
const std::string& text,
ui::ColorId color_id = ui::kColorLabelForegroundSecondary)
: QuickAnswerText(text, color_id) {}
};
struct QuickAnswerImage : public QuickAnswerUiElement {
explicit QuickAnswerImage(const gfx::Image& image)
: QuickAnswerUiElement(QuickAnswerUiElementType::kImage), image(image) {}
gfx::Image image;
};
// Class to describe quick answers phonetics info.
struct PhoneticsInfo {
public:
PhoneticsInfo();
PhoneticsInfo(const PhoneticsInfo&);
~PhoneticsInfo();
// Pronunciation of a word, i.e. in phonetic symbols.
std::string text;
// Phonetics audio URL for playing pronunciation of dictionary results.
// For other type of results the URL will be empty.
GURL phonetics_audio;
// Set to true if tts audio (`query_text` and `locale`) can be used.
// TODO(b/346794579): remove this field.
bool tts_audio_enabled = false;
// Query text and locale which will be used for tts if enabled and
// there is no phonetics audio available.
std::string query_text;
std::string locale;
bool PhoneticsInfoAvailable() const;
bool AudioUrlAvailable() const;
bool TtsAudioAvailable() const;
};
// Structure to describe a quick answer.
struct QuickAnswer {
QuickAnswer();
~QuickAnswer();
ResultType result_type = ResultType::kNoResult;
std::vector<std::unique_ptr<QuickAnswerUiElement>> title;
std::vector<std::unique_ptr<QuickAnswerUiElement>> first_answer_row;
std::unique_ptr<QuickAnswerImage> image;
PhoneticsInfo phonetics_info;
};
// Information of the device that used by the user to send the request.
struct DeviceProperties {
// Whether the request is send by an internal user.
bool is_internal = false;
};
struct IntentInfo {
IntentInfo();
IntentInfo(const IntentInfo& other);
IntentInfo(const std::string& intent_text,
IntentType intent_type,
const std::string& device_language = std::string(),
const std::string& source_language = std::string());
~IntentInfo();
// The text extracted from the selected_text associated with the intent.
std::string intent_text;
// Predicted intent.
IntentType intent_type = IntentType::kUnknown;
// Device language code.
std::string device_language;
// Source language for definition or translation query, should only be used
// for definition or translation intents.
std::string source_language;
};
// Extract information generated from |QuickAnswersRequest|.
struct PreprocessedOutput {
PreprocessedOutput();
PreprocessedOutput(const PreprocessedOutput& other);
~PreprocessedOutput();
IntentInfo intent_info;
// Rewritten query based on |intent_type| and |intent_text|.
std::string query;
};
// Structure of quick answers request context, including device properties and
// surrounding text.
struct Context {
// Device specific properties.
DeviceProperties device_properties;
std::string surrounding_text;
};
// Structure to describe an quick answer request including selected content and
// context.
struct QuickAnswersRequest {
QuickAnswersRequest();
QuickAnswersRequest(const QuickAnswersRequest& other);
~QuickAnswersRequest();
// The selected text.
std::string selected_text;
// Output of processed result.
PreprocessedOutput preprocessed_output;
// Context information.
Context context;
// TODO(b/169346016): Add context and other targeted objects (e.g: images,
// links, etc).
};
// `Sense` must be copyable as a member of `DefinitionResult`.
struct Sense {
public:
Sense();
Sense(const Sense& other);
Sense& operator=(const Sense& other);
~Sense();
std::string definition;
// Not every word sense will have a sample sentence or synonyms.
std::optional<std::string> sample_sentence;
std::optional<std::vector<std::string>> synonyms_list;
};
// `DefinitionResult` holds result for definition intent.
// `DefinitionResult` must be copyable.
struct DefinitionResult {
public:
DefinitionResult();
DefinitionResult(const DefinitionResult& other);
DefinitionResult& operator=(const DefinitionResult& other);
~DefinitionResult();
std::string word;
std::string word_class;
PhoneticsInfo phonetics_info;
Sense sense;
// Not every word will have subsenses.
std::optional<std::vector<Sense>> subsenses_list;
};
// `TranslationResult` holds result for translation intent.
// `TranslationResult` must be copyable.
struct TranslationResult {
public:
TranslationResult();
TranslationResult(const TranslationResult& other);
TranslationResult& operator=(const TranslationResult& other);
~TranslationResult();
std::string text_to_translate;
std::string translated_text;
std::string source_locale;
std::string target_locale;
};
// A unit conversion rule between a unit and the standard SI unit
// of the same category.
// `ConversionRule` must be copyable as a member of `UnitConversion`.
class ConversionRule {
public:
ConversionRule(const ConversionRule& other);
ConversionRule& operator=(const ConversionRule& other);
~ConversionRule();
// Build a `ConversionRule` – returns nullopt if invalid conversion variables.
static std::optional<ConversionRule> Create(
const std::string& category,
const std::string& unit_name,
const std::optional<double>& term_a,
const std::optional<double>& term_b,
const std::optional<double>& term_c);
double ConvertAmountToSi(double unit_amount) const;
double ConvertAmountFromSi(double si_amount) const;
bool IsSingleVariableLinearConversion() const;
double term_a() const { return term_a_; }
const std::string& category() const { return category_; }
const std::string& unit_name() const { return unit_name_; }
private:
ConversionRule(const std::string& category,
const std::string& unit_name,
double term_a,
double term_b,
double term_c);
std::string category_;
std::string unit_name_;
// Conversion formulas are in one of the two formula formats:
// 1. 'target = a * source + b'
// 2. 'target = c / source'
// where a corresponds with |term_a_|, b corresponds with |term_b_|, and c
// corresponds with |term_c_|.
// If |term_a_| is a valid value then |term_c_| must be an invalid value, and
// vice versa.
//
// If |term_a_| is not |kInvalidRateTermValue|, then the conversion formula is
// 'target = a * source + b'
double term_a_;
double term_b_;
// If |term_c_| is not |kInvalidRateTermValue|, then the conversion formula is
// 'target = c / source'
double term_c_;
};
// A unit conversion between two units in the same category.
// `UnitConversion` must be copyable as a member of `UnitConversionResult`.
class UnitConversion {
public:
UnitConversion(const UnitConversion& other);
UnitConversion& operator=(const UnitConversion& other);
~UnitConversion();
// Build a `UnitConversion` – returns nullopt if different unit categories.
static std::optional<UnitConversion> Create(const ConversionRule& source_rule,
const ConversionRule& dest_rule);
// Used for sorting alternative unit conversions.
//
// We have no direct way of comparing unit conversions with different
// formulas. The best approximation is to limit comparisons to linear
// formulas where we only consider the |term_a_| values. A
// smaller a1/a2 ratio between the |source_rule_| and |dest_rule_| of a
// `UnitConversion` is understood as a "smaller" unit conversion.
//
// Unit conversions involving non-linear formulas will be considered greater
// by default for our purposes.
bool operator<(const UnitConversion& other) const;
// Given a |source_amount| in the source unit, returns the equivalent amount
// in the destination unit.
double ConvertSourceAmountToDestAmount(double source_amount) const;
// Function to build the formula description text shown on the Quick Answers
// card. Return nullopt if no simple formula can be derived.
std::optional<std::string> GetConversionFormulaText() const;
const std::string& category() const { return source_rule_.category(); }
const ConversionRule& source_rule() const { return source_rule_; }
const ConversionRule& dest_rule() const { return dest_rule_; }
private:
UnitConversion(const ConversionRule& source_rule,
const ConversionRule& dest_rule);
ConversionRule source_rule_;
ConversionRule dest_rule_;
};
// `UnitConversionResult` holds result for unit conversion intent.
// `UnitConversionResult` must be copyable.
struct UnitConversionResult {
public:
UnitConversionResult();
UnitConversionResult(const UnitConversionResult& other);
UnitConversionResult& operator=(const UnitConversionResult& other);
~UnitConversionResult();
std::string source_text;
std::string result_text;
std::string category;
double source_amount = 0;
// Not every unit conversion result will have valid unit conversions.
std::optional<UnitConversion> source_to_dest_unit_conversion;
std::vector<UnitConversion> alternative_unit_conversions_list;
};
// `StructuredResult` is NOT copyable as it's not trivial to make a class with
// unique_ptr to copyable.
class StructuredResult {
public:
StructuredResult();
~StructuredResult();
StructuredResult(const StructuredResult&) = delete;
StructuredResult& operator=(const StructuredResult) = delete;
ResultType GetResultType() const;
// Result type specific structs must be copyable as they can be copied to
// views.
std::unique_ptr<TranslationResult> translation_result;
std::unique_ptr<DefinitionResult> definition_result;
std::unique_ptr<UnitConversionResult> unit_conversion_result;
};
// `QuickAnswersSession` holds states related to a single Quick Answer session.
//
// This class currently holds results in `QuickAnswer` and `StructuredResult`.
// `QuickAnswer` field is used by `QuickAnswersView`. Rich Answers will read
// `StructuredResult`. Note that `QuickAnswer` is populated by using information
// in `StructuredResult`, i.e. `StructuredResult` is a super-set of
// `QuickAnswer`.
//
// Longer term plan is to migrate other states to this class, e.g. intent.
//
// `QuickAnswersSession` is NOT copyable as it's not trivial to make a class
// with unique_ptr to copyable.
class QuickAnswersSession {
public:
QuickAnswersSession();
~QuickAnswersSession();
QuickAnswersSession(const QuickAnswersSession&) = delete;
QuickAnswersSession& operator=(const QuickAnswersSession) = delete;
// TODO(b/278929409): Once we migrate all result types to `StructuredResult`,
// populate `QuickAnswer` outside of ResultParsers.
std::unique_ptr<QuickAnswer> quick_answer;
std::unique_ptr<StructuredResult> structured_result;
};
} // namespace quick_answers
#endif // CHROMEOS_COMPONENTS_QUICK_ANSWERS_QUICK_ANSWERS_MODEL_H_