// 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 CHROME_BROWSER_ASH_INPUT_METHOD_ASSISTIVE_SUGGESTER_H_
#define CHROME_BROWSER_ASH_INPUT_METHOD_ASSISTIVE_SUGGESTER_H_
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
#include "chrome/browser/ash/input_method/assistive_suggester_switch.h"
#include "chrome/browser/ash/input_method/emoji_suggester.h"
#include "chrome/browser/ash/input_method/longpress_control_v_suggester.h"
#include "chrome/browser/ash/input_method/longpress_diacritics_suggester.h"
#include "chrome/browser/ash/input_method/multi_word_suggester.h"
#include "chrome/browser/ash/input_method/suggester.h"
#include "chrome/browser/ash/input_method/suggestion_enums.h"
#include "chrome/browser/ash/input_method/suggestion_handler_interface.h"
#include "chrome/browser/ash/input_method/suggestions_source.h"
#include "chromeos/ash/services/ime/public/cpp/assistive_suggestions.h"
namespace ash::input_method {
enum class AssistiveSuggesterKeyResult {
// The key event was not handled by the assistive suggester.
// The key event should be handled via normal key event flow.
kNotHandled,
// The key event was handled by the assistive suggester.
// The key event should not be propagated as-is. Instead, it should be
// dispatched as a PROCESS key to prevent the client from triggering the
// default behaviour for the key.
kHandled,
// Same as not kNotHandled, except the key event should not trigger
// autorepeat.
kNotHandledSuppressAutoRepeat,
};
// An agent to suggest assistive information when the user types, and adopt or
// dismiss the suggestion according to the user action.
class AssistiveSuggester : public SuggestionsSource {
public:
// Features handled by assistive suggester.
enum class AssistiveFeature {
kUnknown, // Includes features not handled by assistive suggester.
kEmojiSuggestion,
kMultiWordSuggestion,
};
AssistiveSuggester(
SuggestionHandlerInterface* suggestion_handler,
Profile* profile,
std::unique_ptr<AssistiveSuggesterSwitch> suggester_switch);
~AssistiveSuggester() override;
bool IsAssistiveFeatureEnabled();
// Fetches enabled suggestions in the current browser context then run
// callback.
void FetchEnabledSuggestionsFromBrowserContextThen(
AssistiveSuggesterSwitch::FetchEnabledSuggestionsCallback callback);
// SuggestionsSource overrides
std::vector<ime::AssistiveSuggestion> GetSuggestions() override;
// Called when a new input engine is activated by the system.
void OnActivate(const std::string& engine_id);
// Called when a text field gains focus, and suggester starts working.
void OnFocus(int context_id, const TextInputMethod::InputContext& context);
// Called when a text field loses focus, and suggester stops working.
void OnBlur();
// Called when a surrounding text is changed.
// Returns true if it changes the surrounding text, e.g. a suggestion is
// generated or dismissed.
void OnSurroundingTextChanged(const std::u16string& text,
gfx::Range selection_range);
// Called when the user pressed a key.
AssistiveSuggesterKeyResult OnKeyEvent(const ui::KeyEvent& event);
// Called when suggestions are generated outside of the assistive framework.
void OnExternalSuggestionsUpdated(
const std::vector<ime::AssistiveSuggestion>& suggestions,
const std::optional<ime::SuggestionsTextContext>& context);
// Accepts the suggestion at a given index if a suggester is currently
// active.
void AcceptSuggestion(size_t index);
// Check if suggestion is being shown.
bool IsSuggestionShown();
EmojiSuggester* get_emoji_suggester_for_testing() {
return &emoji_suggester_;
}
std::optional<AssistiveSuggesterSwitch::EnabledSuggestions>
get_enabled_suggestion_from_last_onfocus_for_testing() {
return enabled_suggestions_from_last_onfocus_;
}
private:
// Callback that is run after enabled_suggestions is received.
void ProcessOnSurroundingTextChanged(
const std::u16string& text,
gfx::Range selection_range,
const AssistiveSuggesterSwitch::EnabledSuggestions& enabled_suggestions);
// Returns if any suggestion text should be displayed according to the
// surrounding text information.
bool TrySuggestWithSurroundingText(
const std::u16string& text,
gfx::Range selection_range,
const AssistiveSuggesterSwitch::EnabledSuggestions& enabled_suggestions);
void DismissSuggestion();
bool IsEmojiSuggestAdditionEnabled();
bool IsEnhancedEmojiSuggestEnabled();
bool IsMultiWordSuggestEnabled();
bool IsExpandedMultiWordSuggestEnabled();
bool IsDiacriticsOnPhysicalKeyboardLongpressEnabled();
// Checks the text before cursor, emits metric if any assistive prefix is
// matched.
void RecordAssistiveMatchMetrics(
const std::u16string& text,
gfx::Range selection_range,
const AssistiveSuggesterSwitch::EnabledSuggestions& enabled_suggestions);
void RecordAssistiveMatchMetricsForAssistiveType(
AssistiveType type,
const AssistiveSuggesterSwitch::EnabledSuggestions& enabled_suggestions);
// Only the first applicable reason in DisabledReason enum is returned.
DisabledReason GetDisabledReasonForEmoji(
const AssistiveSuggesterSwitch::EnabledSuggestions& enabled_suggestions);
// Only the first applicable reason in DisabledReason enum is returned.
DisabledReason GetDisabledReasonForMultiWord(
const AssistiveSuggesterSwitch::EnabledSuggestions& enabled_suggestions);
AssistiveFeature GetAssistiveFeatureForType(AssistiveType type);
bool IsAssistiveTypeEnabled(AssistiveType type);
bool IsAssistiveTypeAllowedInBrowserContext(
AssistiveType type,
const AssistiveSuggesterSwitch::EnabledSuggestions& enabled_suggestions);
bool WithinGrammarFragment();
void ProcessExternalSuggestions(
const std::vector<ime::AssistiveSuggestion>& suggestions,
const std::optional<ime::SuggestionsTextContext>& context,
const AssistiveSuggesterSwitch::EnabledSuggestions& enabled_suggestions);
// This records any text input state metrics for each relevant assistive
// feature. It is called once when a text field gains focus.
void RecordTextInputStateMetrics(
const AssistiveSuggesterSwitch::EnabledSuggestions& enabled_suggestions);
// Does longpress related processing (if enabled).
// Returns true if we block the keyevent from passing to IME, and stop
// dispatch.
// Returns false, if we want IME to process the event and dispatch it.
AssistiveSuggesterKeyResult HandleLongpressEnabledKeyEvent(
const ui::KeyEvent& key_character);
void HandleEnabledSuggestionsOnFocus(
const AssistiveSuggesterSwitch::EnabledSuggestions& enabled_suggestions);
void OnLongpressDetected();
// Accepts or dismisses a Ctrl+V long-press suggestion based on the exit
// status of the clipboard history menu, as indicated by `will_paste_item`.
void OnClipboardHistoryMenuClosing(bool will_paste_item);
raw_ptr<Profile> profile_;
EmojiSuggester emoji_suggester_;
MultiWordSuggester multi_word_suggester_;
LongpressDiacriticsSuggester longpress_diacritics_suggester_;
LongpressControlVSuggester longpress_control_v_suggester_;
std::unique_ptr<AssistiveSuggesterSwitch> suggester_switch_;
// The id of the currently active input engine.
std::string active_engine_id_;
// ID of the focused text field, nullopt if none focused.
std::optional<int> focused_context_id_;
// KeyEvent of the held down key at key down. nullopt if no longpress in
// progress.
std::optional<ui::KeyEvent> current_longpress_keydown_;
// Timer for longpress. Starts when key is held down. Fires when successfully
// held down for a specified longpress duration.
base::OneShotTimer longpress_timer_;
// The current suggester in use, nullptr means no suggestion is shown.
raw_ptr<Suggester> current_suggester_ = nullptr;
std::optional<AssistiveSuggesterSwitch::EnabledSuggestions>
enabled_suggestions_from_last_onfocus_;
std::u16string last_surrounding_text_ = u"";
// Keeps track if there is a key being held down currently which was already
// recorded for the auto repeat suppressed metric.
bool auto_repeat_suppress_metric_emitted_ = false;
int last_cursor_pos_ = 0;
TextInputMethod::InputContext context_;
base::WeakPtrFactory<AssistiveSuggester> weak_ptr_factory_{this};
};
} // namespace ash::input_method
#endif // CHROME_BROWSER_ASH_INPUT_METHOD_ASSISTIVE_SUGGESTER_H_