assistant_ui_controller_impl.cc 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/assistant/assistant_ui_controller_impl.h"
  5. #include "ash/assistant/assistant_controller_impl.h"
  6. #include "ash/assistant/model/assistant_interaction_model.h"
  7. #include "ash/assistant/ui/assistant_ui_constants.h"
  8. #include "ash/assistant/util/assistant_util.h"
  9. #include "ash/assistant/util/deep_link_util.h"
  10. #include "ash/assistant/util/histogram_util.h"
  11. #include "ash/constants/ash_pref_names.h"
  12. #include "ash/constants/notifier_catalogs.h"
  13. #include "ash/public/cpp/assistant/assistant_setup.h"
  14. #include "ash/public/cpp/assistant/assistant_state.h"
  15. #include "ash/public/cpp/assistant/controller/assistant_interaction_controller.h"
  16. #include "ash/public/cpp/system/toast_data.h"
  17. #include "ash/session/session_controller_impl.h"
  18. #include "ash/shell.h"
  19. #include "ash/strings/grit/ash_strings.h"
  20. #include "ash/system/toast/toast_manager_impl.h"
  21. #include "base/bind.h"
  22. #include "base/metrics/histogram_functions.h"
  23. #include "chromeos/ash/services/assistant/public/cpp/assistant_prefs.h"
  24. #include "chromeos/ash/services/assistant/public/cpp/assistant_service.h"
  25. #include "chromeos/ash/services/assistant/public/cpp/features.h"
  26. #include "components/prefs/pref_registry_simple.h"
  27. #include "components/prefs/pref_service.h"
  28. #include "third_party/abseil-cpp/absl/types/optional.h"
  29. #include "ui/base/l10n/l10n_util.h"
  30. namespace ash {
  31. namespace {
  32. // Helpers ---------------------------------------------------------------------
  33. PrefService* pref_service() {
  34. auto* result =
  35. Shell::Get()->session_controller()->GetPrimaryUserPrefService();
  36. DCHECK(result);
  37. return result;
  38. }
  39. // Toast -----------------------------------------------------------------------
  40. constexpr char kStylusPromptToastId[] = "stylus_prompt_for_embedded_ui";
  41. constexpr char kUnboundServiceToastId[] =
  42. "assistant_controller_unbound_service";
  43. void ShowToast(const std::string& id,
  44. ToastCatalogName catalog_name,
  45. int message_id) {
  46. ToastData toast(id, catalog_name, l10n_util::GetStringUTF16(message_id));
  47. Shell::Get()->toast_manager()->Show(toast);
  48. }
  49. } // namespace
  50. // AssistantUiControllerImpl ---------------------------------------------------
  51. AssistantUiControllerImpl::AssistantUiControllerImpl(
  52. AssistantControllerImpl* assistant_controller)
  53. : assistant_controller_(assistant_controller) {
  54. model_.AddObserver(this);
  55. assistant_controller_observation_.Observe(AssistantController::Get());
  56. highlighter_controller_observation_.Observe(
  57. Shell::Get()->highlighter_controller());
  58. overview_controller_observation_.Observe(Shell::Get()->overview_controller());
  59. }
  60. AssistantUiControllerImpl::~AssistantUiControllerImpl() {
  61. model_.RemoveObserver(this);
  62. }
  63. // static
  64. void AssistantUiControllerImpl::RegisterProfilePrefs(
  65. PrefRegistrySimple* registry) {
  66. registry->RegisterIntegerPref(
  67. prefs::kAssistantNumSessionsWhereOnboardingShown, 0);
  68. }
  69. void AssistantUiControllerImpl::SetAssistant(assistant::Assistant* assistant) {
  70. assistant_ = assistant;
  71. }
  72. const AssistantUiModel* AssistantUiControllerImpl::GetModel() const {
  73. return &model_;
  74. }
  75. int AssistantUiControllerImpl::GetNumberOfSessionsWhereOnboardingShown() const {
  76. return pref_service()->GetInteger(
  77. prefs::kAssistantNumSessionsWhereOnboardingShown);
  78. }
  79. bool AssistantUiControllerImpl::HasShownOnboarding() const {
  80. return has_shown_onboarding_;
  81. }
  82. void AssistantUiControllerImpl::SetKeyboardTraversalMode(
  83. bool keyboard_traversal_mode) {
  84. model_.SetKeyboardTraversalMode(keyboard_traversal_mode);
  85. }
  86. void AssistantUiControllerImpl::ShowUi(AssistantEntryPoint entry_point) {
  87. // Skip if the opt-in window is active.
  88. auto* assistant_setup = AssistantSetup::GetInstance();
  89. if (assistant_setup && assistant_setup->BounceOptInWindowIfActive())
  90. return;
  91. auto* assistant_state = AssistantState::Get();
  92. if (!assistant_state->settings_enabled().value_or(false) ||
  93. assistant_state->locked_full_screen_enabled().value_or(false)) {
  94. return;
  95. }
  96. // TODO(dmblack): Show a more helpful message to the user.
  97. if (assistant_state->assistant_status() ==
  98. assistant::AssistantStatus::NOT_READY) {
  99. ShowUnboundErrorToast();
  100. return;
  101. }
  102. if (!assistant_) {
  103. ShowUnboundErrorToast();
  104. return;
  105. }
  106. model_.SetVisible(entry_point);
  107. }
  108. absl::optional<base::ScopedClosureRunner> AssistantUiControllerImpl::CloseUi(
  109. AssistantExitPoint exit_point) {
  110. if (model_.visibility() != AssistantVisibility::kVisible)
  111. return absl::nullopt;
  112. // Set visibility to `kClosing`.
  113. model_.SetClosing(exit_point);
  114. // When the return value is destroyed, visibility will be set to `kClosed`
  115. // provided the visibility change hasn't been invalidated.
  116. return base::ScopedClosureRunner(base::BindOnce(
  117. [](const base::WeakPtr<AssistantUiControllerImpl>& weak_ptr,
  118. assistant::AssistantExitPoint exit_point) {
  119. if (weak_ptr)
  120. weak_ptr->model_.SetClosed(exit_point);
  121. },
  122. weak_factory_for_delayed_visibility_changes_.GetWeakPtr(), exit_point));
  123. }
  124. void AssistantUiControllerImpl::SetAppListBubbleWidth(int width) {
  125. model_.SetAppListBubbleWidth(width);
  126. }
  127. void AssistantUiControllerImpl::ToggleUi(
  128. absl::optional<AssistantEntryPoint> entry_point,
  129. absl::optional<AssistantExitPoint> exit_point) {
  130. // When not visible, toggling will show the UI.
  131. if (model_.visibility() != AssistantVisibility::kVisible) {
  132. DCHECK(entry_point.has_value());
  133. ShowUi(entry_point.value());
  134. return;
  135. }
  136. // Otherwise toggling closes the UI.
  137. DCHECK(exit_point.has_value());
  138. CloseUi(exit_point.value());
  139. }
  140. void AssistantUiControllerImpl::OnInteractionStateChanged(
  141. InteractionState interaction_state) {
  142. if (interaction_state != InteractionState::kActive)
  143. return;
  144. // If there is an active interaction, we need to show Assistant UI if it is
  145. // not already showing. We don't have enough information here to know what
  146. // the interaction source is.
  147. ShowUi(AssistantEntryPoint::kUnspecified);
  148. }
  149. void AssistantUiControllerImpl::OnAssistantControllerConstructed() {
  150. AssistantInteractionController::Get()->GetModel()->AddObserver(this);
  151. assistant_controller_->view_delegate()->AddObserver(this);
  152. }
  153. void AssistantUiControllerImpl::OnAssistantControllerDestroying() {
  154. assistant_controller_->view_delegate()->RemoveObserver(this);
  155. AssistantInteractionController::Get()->GetModel()->RemoveObserver(this);
  156. }
  157. void AssistantUiControllerImpl::OnOpeningUrl(const GURL& url,
  158. bool in_background,
  159. bool from_server) {
  160. if (model_.visibility() != AssistantVisibility::kVisible)
  161. return;
  162. CloseUi(from_server ? AssistantExitPoint::kNewBrowserTabFromServer
  163. : AssistantExitPoint::kNewBrowserTabFromUser);
  164. }
  165. void AssistantUiControllerImpl::OnUiVisibilityChanged(
  166. AssistantVisibility new_visibility,
  167. AssistantVisibility old_visibility,
  168. absl::optional<AssistantEntryPoint> entry_point,
  169. absl::optional<AssistantExitPoint> exit_point) {
  170. weak_factory_for_delayed_visibility_changes_.InvalidateWeakPtrs();
  171. if (new_visibility == AssistantVisibility::kVisible) {
  172. // Only record the entry point when Assistant UI becomes visible.
  173. assistant::util::RecordAssistantEntryPoint(entry_point.value());
  174. }
  175. if (old_visibility == AssistantVisibility::kVisible) {
  176. // Metalayer should not be sticky. Disable when the UI is no longer visible.
  177. if (exit_point != AssistantExitPoint::kStylus)
  178. Shell::Get()->highlighter_controller()->AbortSession();
  179. // Only record the exit point when Assistant UI becomes invisible to
  180. // avoid recording duplicate events (e.g. pressing ESC key).
  181. assistant::util::RecordAssistantExitPoint(exit_point.value());
  182. }
  183. }
  184. void AssistantUiControllerImpl::OnOnboardingShown() {
  185. if (has_shown_onboarding_)
  186. return;
  187. has_shown_onboarding_ = true;
  188. // Update the number of user sessions in which Assistant onboarding was shown.
  189. pref_service()->SetInteger(prefs::kAssistantNumSessionsWhereOnboardingShown,
  190. GetNumberOfSessionsWhereOnboardingShown() + 1);
  191. }
  192. void AssistantUiControllerImpl::OnHighlighterEnabledChanged(
  193. HighlighterEnabledState state) {
  194. if (state != HighlighterEnabledState::kEnabled)
  195. return;
  196. ShowToast(kStylusPromptToastId, ToastCatalogName::kStylusPrompt,
  197. IDS_ASH_ASSISTANT_PROMPT_STYLUS);
  198. CloseUi(AssistantExitPoint::kStylus);
  199. }
  200. void AssistantUiControllerImpl::OnOverviewModeWillStart() {
  201. // Close Assistant UI before entering overview mode.
  202. CloseUi(AssistantExitPoint::kOverviewMode);
  203. }
  204. void AssistantUiControllerImpl::ShowUnboundErrorToast() {
  205. ShowToast(kUnboundServiceToastId, ToastCatalogName::kAssistantUnboundService,
  206. IDS_ASH_ASSISTANT_ERROR_GENERIC);
  207. }
  208. } // namespace ash