| // Copyright 2023 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef ASH_QUICK_INSERT_QUICK_INSERT_CONTROLLER_H_ | |
| #define ASH_QUICK_INSERT_QUICK_INSERT_CONTROLLER_H_ | |
| #include <memory> | |
| #include <optional> | |
| #include <string> | |
| #include <string_view> | |
| #include <vector> | |
| #include "ash/ash_export.h" | |
| #include "ash/quick_insert/metrics/quick_insert_feature_usage_metrics.h" | |
| #include "ash/quick_insert/metrics/quick_insert_session_metrics.h" | |
| #include "ash/quick_insert/model/quick_insert_emoji_history_model.h" | |
| #include "ash/quick_insert/model/quick_insert_emoji_suggester.h" | |
| #include "ash/quick_insert/model/quick_insert_model.h" | |
| #include "ash/quick_insert/quick_insert_asset_fetcher_impl_delegate.h" | |
| #include "ash/quick_insert/quick_insert_caps_lock_bubble_controller.h" | |
| #include "ash/quick_insert/quick_insert_insert_media_request.h" | |
| #include "ash/quick_insert/quick_insert_search_result.h" | |
| #include "ash/quick_insert/quick_insert_suggestions_controller.h" | |
| #include "ash/quick_insert/quick_insert_web_paste_target.h" | |
| #include "ash/quick_insert/search/quick_insert_search_controller.h" | |
| #include "ash/quick_insert/views/quick_insert_feature_tour.h" | |
| #include "ash/quick_insert/views/quick_insert_view_delegate.h" | |
| #include "base/functional/callback_forward.h" | |
| #include "base/memory/scoped_refptr.h" | |
| #include "base/memory/weak_ptr.h" | |
| #include "base/scoped_observation.h" | |
| #include "base/time/time.h" | |
| #include "base/timer/timer.h" | |
| #include "ui/base/emoji/emoji_panel_helper.h" | |
| #include "ui/events/devices/device_data_manager.h" | |
| #include "ui/events/devices/input_device_event_observer.h" | |
| #include "ui/views/view_observer.h" | |
| #include "ui/views/widget/unique_widget_ptr.h" | |
| class PrefRegistrySimple; | |
| class PrefService; | |
| namespace input_method { | |
| class ImeKeyboard; | |
| } | |
| namespace ui { | |
| class TextInputClient; | |
| } | |
| namespace network { | |
| class SharedURLLoaderFactory; | |
| } // namespace network | |
| namespace ash { | |
| class QuickInsertAssetFetcher; | |
| class QuickInsertClient; | |
| class QuickInsertModel; | |
| class QuickInsertPasteRequest; | |
| class QuickInsertActionOnNextFocusRequest; | |
| // Controls a Quick Insert widget. | |
| class ASH_EXPORT QuickInsertController | |
| : public QuickInsertViewDelegate, | |
| public views::ViewObserver, | |
| public QuickInsertAssetFetcherImplDelegate { | |
| public: | |
| QuickInsertController(); | |
| QuickInsertController(const QuickInsertController&) = delete; | |
| QuickInsertController& operator=(const QuickInsertController&) = delete; | |
| ~QuickInsertController() override; | |
| // Maximum time to wait for focus to be regained after completing the feature | |
| // tour. If this timeout is reached, we stop waiting for focus and show the | |
| // Quick Insert widget regardless of the focus state. | |
| static constexpr base::TimeDelta kShowWidgetPostFeatureTourTimeout = | |
| base::Seconds(2); | |
| // Time from when the insert is issued and when we give up inserting. | |
| static constexpr base::TimeDelta kInsertMediaTimeout = base::Seconds(2); | |
| // Time from when a search starts to when the first set of results are | |
| // published. | |
| static constexpr base::TimeDelta kBurnInPeriod = base::Milliseconds(200); | |
| // Registers Quick Insert prefs to the provided `registry`. | |
| static void RegisterProfilePrefs(PrefRegistrySimple* registry); | |
| // Sets the `client` used by this class and the widget to communicate with the | |
| // browser. `client` may be set to null, which will close the Widget if it's | |
| // open, and may call "stop search" methods on the PREVIOUS client. | |
| // If `client` is not null, then it must remain valid for the lifetime of this | |
| // class, or until AFTER `SetClient` is called with a different client. | |
| // Caution: If `client` outlives this class, the client should avoid calling | |
| // this method on a destructed class instance to avoid a use after free. | |
| void SetClient(QuickInsertClient* client); | |
| // This should be run when the Prefs from the client is ready. | |
| void OnClientPrefsSet(PrefService* prefs); | |
| // Toggles the visibility of the Quick Insert widget. | |
| // This must only be called after `SetClient` is called with a valid client. | |
| // `trigger_event_timestamp` is the timestamp of the event that triggered the | |
| // Widget to be toggled. For example, if the feature was triggered by a mouse | |
| // click, then it should be the timestamp of the click. By default, the | |
| // timestamp is the time this function is called. | |
| void ToggleWidget( | |
| base::TimeTicks trigger_event_timestamp = base::TimeTicks::Now()); | |
| // Returns the Quick Insert widget for tests. | |
| views::Widget* widget_for_testing() { return widget_.get(); } | |
| QuickInsertFeatureTour& feature_tour_for_testing() { return feature_tour_; } | |
| QuickInsertCapsLockBubbleController& | |
| caps_lock_bubble_controller_for_testing() { | |
| return caps_lock_bubble_controller_; | |
| } | |
| // QuickInsertViewDelegate: | |
| std::vector<QuickInsertCategory> GetAvailableCategories() override; | |
| void GetZeroStateSuggestedResults(SuggestedResultsCallback callback) override; | |
| void GetResultsForCategory(QuickInsertCategory category, | |
| SearchResultsCallback callback) override; | |
| void StartSearch(std::u16string_view query, | |
| std::optional<QuickInsertCategory> category, | |
| SearchResultsCallback callback) override; | |
| void StopSearch() override; | |
| void StartEmojiSearch(std::u16string_view, | |
| EmojiSearchResultsCallback callback) override; | |
| void CloseWidgetThenInsertResultOnNextFocus( | |
| const QuickInsertSearchResult& result) override; | |
| void OpenResult(const QuickInsertSearchResult& result) override; | |
| void ShowEmojiPicker(ui::EmojiPickerCategory category, | |
| std::u16string_view query) override; | |
| void ShowEditor(std::optional<std::string> preset_query_id, | |
| std::optional<std::string> freeform_text) override; | |
| void ShowLobster(std::optional<std::string> freeform_text) override; | |
| QuickInsertAssetFetcher* GetAssetFetcher() override; | |
| QuickInsertSessionMetrics& GetSessionMetrics() override; | |
| QuickInsertActionType GetActionForResult( | |
| const QuickInsertSearchResult& result) override; | |
| std::vector<QuickInsertEmojiResult> GetSuggestedEmoji() override; | |
| bool IsGifsEnabled() override; | |
| QuickInsertModeType GetMode() override; | |
| QuickInsertCapsLockPosition GetCapsLockPosition() override; | |
| // views:ViewObserver: | |
| void OnViewIsDeleting(views::View* view) override; | |
| // QuickInsertAssetFetcherImplDelegate: | |
| scoped_refptr<network::SharedURLLoaderFactory> GetSharedURLLoaderFactory() | |
| override; | |
| void FetchFileThumbnail(const base::FilePath& path, | |
| const gfx::Size& size, | |
| FetchFileThumbnailCallback callback) override; | |
| // Disables the feature tour. Only works in tests. | |
| static void DisableFeatureTourForTesting(); | |
| private: | |
| // Trigger source for showing the Quick Insert widget. This is used to | |
| // determine how the widget should be shown on the screen. | |
| enum class WidgetTriggerSource { | |
| // The user triggered Quick Insert as part of their usual user flow, e.g. | |
| // toggled Quick Insert with a key press. | |
| kDefault, | |
| // The user triggered Quick Insert by completing the feature tour. | |
| kFeatureTour, | |
| }; | |
| // Active Quick Insert session tied to the lifetime of the QuickInsertWidget. | |
| struct Session { | |
| QuickInsertModel model; | |
| QuickInsertEmojiHistoryModel emoji_history_model; | |
| QuickInsertEmojiSuggester emoji_suggester; | |
| QuickInsertSessionMetrics session_metrics; | |
| // Periodically records usage metrics based on the Standard Feature Usage | |
| // Logging (SFUL) framework. | |
| QuickInsertFeatureUsageMetrics feature_usage_metrics; | |
| Session(PrefService* prefs, | |
| ui::TextInputClient* focused_client, | |
| input_method::ImeKeyboard* ime_keyboard, | |
| QuickInsertModel::EditorStatus editor_status, | |
| QuickInsertModel::LobsterStatus lobster_status, | |
| QuickInsertEmojiSuggester::GetNameCallback get_name); | |
| ~Session(); | |
| }; | |
| void ShowWidget(base::TimeTicks trigger_event_timestamp, | |
| WidgetTriggerSource trigger_source); | |
| void CloseWidget(); | |
| void ShowWidgetPostFeatureTour(); | |
| void InsertResultOnNextFocus(const QuickInsertSearchResult& result); | |
| void OnInsertCompleted(const QuickInsertRichMedia& media, | |
| QuickInsertInsertMediaRequest::Result result); | |
| PrefService* GetPrefs(); | |
| std::optional<QuickInsertWebPasteTarget> GetWebPasteTarget(); | |
| QuickInsertFeatureTour feature_tour_; | |
| QuickInsertCapsLockBubbleController caps_lock_bubble_controller_; | |
| std::unique_ptr<Session> session_; | |
| views::UniqueWidgetPtr widget_; | |
| std::unique_ptr<QuickInsertAssetFetcher> asset_fetcher_; | |
| std::unique_ptr<QuickInsertInsertMediaRequest> insert_media_request_; | |
| std::unique_ptr<QuickInsertPasteRequest> paste_request_; | |
| std::unique_ptr<QuickInsertActionOnNextFocusRequest> caps_lock_request_; | |
| QuickInsertSuggestionsController suggestions_controller_; | |
| QuickInsertSearchController search_controller_; | |
| raw_ptr<QuickInsertClient> client_ = nullptr; | |
| base::OnceCallback<void(std::optional<std::string> preset_query_id, | |
| std::optional<std::string> freeform_text)> | |
| show_editor_callback_; | |
| base::OnceCallback<void(std::optional<std::string> freeform_text)> | |
| show_lobster_callback_; | |
| // Timer used to delay closing the Widget for accessibility. | |
| base::OneShotTimer close_widget_delay_timer_; | |
| base::ScopedObservation<views::View, views::ViewObserver> view_observation_{ | |
| this}; | |
| base::WeakPtrFactory<QuickInsertController> weak_ptr_factory_{this}; | |
| }; | |
| } // namespace ash | |
| #endif // ASH_QUICK_INSERT_QUICK_INSERT_CONTROLLER_H_ |