1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233

ash / quick_insert / quick_insert_controller.h [blame]

// 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_