Commit 68a5784c authored by Jeroen Dhollander's avatar Jeroen Dhollander Committed by Commit Bot

Disable suggestion chips after submitting query

When an Assistant query has been submitted, you should not be able to
interact with the suggestion chips anymore.
Mouse clicks had always been disabled, but a bug still allowed the
user to submit queries using |TAB| and |ENTER|.

This CL also introduces some extra unittests that were missing.

Bug: b/151800188
Change-Id: I15fe1b946ae92c1f74b7a98656793e4d00b2a12c
Tests: ash_unittests "AssistantPageViewTest.*" and view_unittests
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2119388Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarXiaohui Chen <xiaohuic@chromium.org>
Commit-Queue: Jeroen Dhollander <jeroendh@google.com>
Cr-Commit-Position: refs/heads/master@{#754711}
parent 4d66384d
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "ash/assistant/model/assistant_ui_model.h" #include "ash/assistant/model/assistant_ui_model.h"
#include "ash/assistant/test/assistant_ash_test_base.h" #include "ash/assistant/test/assistant_ash_test_base.h"
#include "ash/assistant/ui/assistant_ui_constants.h" #include "ash/assistant/ui/assistant_ui_constants.h"
#include "ash/assistant/ui/main_stage/suggestion_chip_view.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "chromeos/services/assistant/public/mojom/assistant.mojom-shared.h" #include "chromeos/services/assistant/public/mojom/assistant.mojom-shared.h"
...@@ -29,6 +30,13 @@ using chromeos::assistant::mojom::AssistantInteractionType; ...@@ -29,6 +30,13 @@ using chromeos::assistant::mojom::AssistantInteractionType;
EXPECT_EQ(interaction->type, type_); \ EXPECT_EQ(interaction->type, type_); \
}) })
#define EXPECT_NO_INTERACTION() \
({ \
base::Optional<AssistantInteractionMetadata> interaction = \
current_interaction(); \
ASSERT_FALSE(interaction.has_value()); \
})
// Ensures that the given view has the focus. If it doesn't, this will print a // Ensures that the given view has the focus. If it doesn't, this will print a
// nice error message indicating which view has the focus instead. // nice error message indicating which view has the focus instead.
#define EXPECT_HAS_FOCUS(expected_) \ #define EXPECT_HAS_FOCUS(expected_) \
...@@ -172,7 +180,9 @@ class AssistantPageViewTest : public AssistantAshTestBase { ...@@ -172,7 +180,9 @@ class AssistantPageViewTest : public AssistantAshTestBase {
} }
void PressKey(ui::KeyboardCode key_code) { void PressKey(ui::KeyboardCode key_code) {
// Any key press consists of 2 events, namely |press| and |release|.
GetEventGenerator()->PressKey(key_code, /*flags=*/ui::EF_NONE); GetEventGenerator()->PressKey(key_code, /*flags=*/ui::EF_NONE);
GetEventGenerator()->ReleaseKey(key_code, /*flags=*/ui::EF_NONE);
} }
void PressKeyAndWait(ui::KeyboardCode key_code) { void PressKeyAndWait(ui::KeyboardCode key_code) {
...@@ -180,6 +190,14 @@ class AssistantPageViewTest : public AssistantAshTestBase { ...@@ -180,6 +190,14 @@ class AssistantPageViewTest : public AssistantAshTestBase {
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
} }
ash::SuggestionChipView* CreateAndGetSuggestionChip(
const std::string& chip_query) {
MockTextInteraction().WithSuggestionChip(chip_query);
auto suggestion_chips = GetSuggestionChips();
DCHECK_EQ(suggestion_chips.size(), 1u);
return suggestion_chips[0];
}
const views::View* GetFocusedView() { const views::View* GetFocusedView() {
return main_view()->GetFocusManager()->GetFocusedView(); return main_view()->GetFocusManager()->GetFocusedView();
} }
...@@ -188,6 +206,54 @@ class AssistantPageViewTest : public AssistantAshTestBase { ...@@ -188,6 +206,54 @@ class AssistantPageViewTest : public AssistantAshTestBase {
DISALLOW_COPY_AND_ASSIGN(AssistantPageViewTest); DISALLOW_COPY_AND_ASSIGN(AssistantPageViewTest);
}; };
// Counts the number of Assistant interactions that are started.
class AssistantInteractionCounter
: private chromeos::assistant::mojom::AssistantInteractionSubscriber {
public:
explicit AssistantInteractionCounter(
chromeos::assistant::mojom::Assistant* service) {
service->AddAssistantInteractionSubscriber(
receiver_.BindNewPipeAndPassRemote());
}
AssistantInteractionCounter(AssistantInteractionCounter&) = delete;
AssistantInteractionCounter& operator=(AssistantInteractionCounter&) = delete;
~AssistantInteractionCounter() override = default;
int interaction_count() const { return interaction_count_; }
private:
// AssistantInteractionSubscriber implementation:
void OnInteractionStarted(
chromeos::assistant::mojom::AssistantInteractionMetadataPtr) override {
interaction_count_++;
}
void OnInteractionFinished(
chromeos::assistant::mojom::AssistantInteractionResolution) override {}
void OnHtmlResponse(const std::string& response,
const std::string& fallback) override {}
void OnSuggestionsResponse(
std::vector<chromeos::assistant::mojom::AssistantSuggestionPtr> response)
override {}
void OnTextResponse(const std::string& response) override {}
void OnTimersResponse(const std::vector<std::string>& timer_ids) override {}
void OnOpenUrlResponse(const ::GURL& url, bool in_background) override {}
void OnOpenAppResponse(chromeos::assistant::mojom::AndroidAppInfoPtr app_info,
OnOpenAppResponseCallback callback) override {}
void OnSpeechRecognitionStarted() override {}
void OnSpeechRecognitionIntermediateResult(
const std::string& high_confidence_text,
const std::string& low_confidence_text) override {}
void OnSpeechRecognitionEndOfUtterance() override {}
void OnSpeechRecognitionFinalResult(
const std::string& final_result) override {}
void OnSpeechLevelUpdated(float speech_level) override {}
void OnTtsStarted(bool due_to_error) override {}
void OnWaitStarted() override {}
mojo::Receiver<AssistantInteractionSubscriber> receiver_{this};
int interaction_count_ = 0;
};
} // namespace } // namespace
TEST_F(AssistantPageViewTest, ShouldStartAtMinimumHeight) { TEST_F(AssistantPageViewTest, ShouldStartAtMinimumHeight) {
...@@ -373,6 +439,146 @@ TEST_F(AssistantPageViewTest, ...@@ -373,6 +439,146 @@ TEST_F(AssistantPageViewTest,
EXPECT_FALSE(current_interaction().has_value()); EXPECT_FALSE(current_interaction().has_value());
} }
TEST_F(AssistantPageViewTest, ShouldShowOptInViewUnlessUserHasGivenConsent) {
ShowAssistantUi();
const views::View* suggestion_chips = suggestion_chip_container();
const views::View* opt_in = opt_in_view();
SetConsentStatus(ConsentStatus::kUnauthorized);
EXPECT_TRUE(opt_in->IsDrawn());
EXPECT_FALSE(suggestion_chips->IsDrawn());
SetConsentStatus(ConsentStatus::kNotFound);
EXPECT_TRUE(opt_in->IsDrawn());
EXPECT_FALSE(suggestion_chips->IsDrawn());
SetConsentStatus(ConsentStatus::kUnknown);
EXPECT_TRUE(opt_in->IsDrawn());
EXPECT_FALSE(suggestion_chips->IsDrawn());
SetConsentStatus(ConsentStatus::kActivityControlAccepted);
EXPECT_FALSE(opt_in->IsDrawn());
EXPECT_TRUE(suggestion_chips->IsDrawn());
}
TEST_F(AssistantPageViewTest, ShouldSubmitQueryWhenClickingOnSuggestionChip) {
ShowAssistantUi();
ash::SuggestionChipView* suggestion_chip =
CreateAndGetSuggestionChip("<suggestion chip query>");
ClickOnAndWait(suggestion_chip);
EXPECT_INTERACTION_OF_TYPE(AssistantInteractionType::kText);
EXPECT_EQ("<suggestion chip query>", current_interaction()->query);
}
TEST_F(AssistantPageViewTest,
ShouldSubmitQueryWhenPressingEnterOnSuggestionChip) {
ShowAssistantUi();
ash::SuggestionChipView* suggestion_chip =
CreateAndGetSuggestionChip("<suggestion chip query>");
suggestion_chip->RequestFocus();
PressKeyAndWait(ui::KeyboardCode::VKEY_RETURN);
EXPECT_INTERACTION_OF_TYPE(AssistantInteractionType::kText);
EXPECT_EQ("<suggestion chip query>", current_interaction()->query);
}
TEST_F(AssistantPageViewTest,
ShouldNotSubmitQueryWhenPressingSpaceOnSuggestionChip) {
ShowAssistantUi();
ash::SuggestionChipView* suggestion_chip =
CreateAndGetSuggestionChip("<suggestion chip query>");
suggestion_chip->RequestFocus();
PressKeyAndWait(ui::KeyboardCode::VKEY_SPACE);
EXPECT_NO_INTERACTION();
}
TEST_F(AssistantPageViewTest,
ShouldOnlySubmitOneQueryWhenClickingSuggestionChipMultipleTimes) {
ShowAssistantUi();
ash::SuggestionChipView* suggestion_chip =
CreateAndGetSuggestionChip("<suggestion chip query>");
AssistantInteractionCounter counter{assistant_service()};
ClickOnAndWait(suggestion_chip, /*check_if_view_can_process_events=*/false);
ClickOnAndWait(suggestion_chip, /*check_if_view_can_process_events=*/false);
ClickOnAndWait(suggestion_chip, /*check_if_view_can_process_events=*/false);
ClickOnAndWait(suggestion_chip, /*check_if_view_can_process_events=*/false);
EXPECT_EQ(1, counter.interaction_count());
}
TEST_F(AssistantPageViewTest,
ShouldOnlySubmitQueryFromFirstSuggestionChipClickedOn) {
ShowAssistantUi();
MockTextInteraction()
.WithSuggestionChip("<first query>")
.WithSuggestionChip("<second query>")
.WithSuggestionChip("<third query>");
auto suggestion_chips = GetSuggestionChips();
AssistantInteractionCounter counter{assistant_service()};
ClickOnAndWait(suggestion_chips[0]);
// All next clicks should be no-ops.
ClickOnAndWait(suggestion_chips[0],
/*check_if_view_can_process_events=*/false);
ClickOnAndWait(suggestion_chips[1],
/*check_if_view_can_process_events=*/false);
ClickOnAndWait(suggestion_chips[2],
/*check_if_view_can_process_events=*/false);
EXPECT_EQ(1, counter.interaction_count());
EXPECT_EQ("<first query>", current_interaction()->query);
}
TEST_F(AssistantPageViewTest,
SuggestionChipsShouldNotBeFocusableAfterSubmittingQuery) {
ShowAssistantUi();
MockTextInteraction()
.WithSuggestionChip("<first query>")
.WithSuggestionChip("<second query>")
.WithSuggestionChip("<third query>");
auto suggestion_chips = GetSuggestionChips();
suggestion_chips[0]->RequestFocus();
PressKeyAndWait(ui::KeyboardCode::VKEY_RETURN);
for (auto* suggestion_chip : suggestion_chips) {
EXPECT_FALSE(suggestion_chip->IsFocusable())
<< "Suggestion chip '" << suggestion_chip->GetText()
<< "' is still focusable";
}
}
TEST_F(AssistantPageViewTest,
ShouldFocusTextFieldWhenSubmittingSuggestionChipInTextMode) {
ShowAssistantUiInTextMode();
ash::SuggestionChipView* suggestion_chip =
CreateAndGetSuggestionChip("<suggestion chip query>");
suggestion_chip->RequestFocus();
PressKeyAndWait(ui::KeyboardCode::VKEY_RETURN);
EXPECT_HAS_FOCUS(input_text_field());
}
TEST_F(AssistantPageViewTest,
ShouldFocusMicWhenSubmittingSuggestionChipInVoiceMode) {
ShowAssistantUi();
ash::SuggestionChipView* suggestion_chip =
CreateAndGetSuggestionChip("<suggestion chip query>");
ClickOnAndWait(voice_input_toggle());
suggestion_chip->RequestFocus();
PressKeyAndWait(ui::KeyboardCode::VKEY_RETURN);
EXPECT_HAS_FOCUS(mic_view());
}
TEST_F(AssistantPageViewTest, TEST_F(AssistantPageViewTest,
ShouldFocusTextFieldWhenPressingKeyboardInputToggle) { ShouldFocusTextFieldWhenPressingKeyboardInputToggle) {
ShowAssistantUiInVoiceMode(); ShowAssistantUiInVoiceMode();
......
...@@ -92,6 +92,10 @@ views::View* AssistantTestApiImpl::keyboard_input_toggle() { ...@@ -92,6 +92,10 @@ views::View* AssistantTestApiImpl::keyboard_input_toggle() {
return page_view()->GetViewByID(AssistantViewID::kKeyboardInputToggle); return page_view()->GetViewByID(AssistantViewID::kKeyboardInputToggle);
} }
views::View* AssistantTestApiImpl::suggestion_chip_container() {
return page_view()->GetViewByID(AssistantViewID::kSuggestionContainer);
}
views::View* AssistantTestApiImpl::opt_in_view() { views::View* AssistantTestApiImpl::opt_in_view() {
return page_view()->GetViewByID(AssistantViewID::kOptInView); return page_view()->GetViewByID(AssistantViewID::kOptInView);
} }
......
...@@ -45,6 +45,7 @@ class AssistantTestApiImpl : public AssistantTestApi { ...@@ -45,6 +45,7 @@ class AssistantTestApiImpl : public AssistantTestApi {
views::View* greeting_label() override; views::View* greeting_label() override;
views::View* voice_input_toggle() override; views::View* voice_input_toggle() override;
views::View* keyboard_input_toggle() override; views::View* keyboard_input_toggle() override;
views::View* suggestion_chip_container() override;
views::View* opt_in_view() override; views::View* opt_in_view() override;
aura::Window* window() override; aura::Window* window() override;
views::View* app_list_view() override; views::View* app_list_view() override;
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "ash/assistant/test/test_assistant_client.h" #include "ash/assistant/test/test_assistant_client.h"
#include "ash/assistant/test/test_assistant_setup.h" #include "ash/assistant/test/test_assistant_setup.h"
#include "ash/assistant/test/test_assistant_web_view_factory.h" #include "ash/assistant/test/test_assistant_web_view_factory.h"
#include "ash/assistant/ui/main_stage/suggestion_chip_view.h"
#include "ash/keyboard/ui/keyboard_ui_controller.h" #include "ash/keyboard/ui/keyboard_ui_controller.h"
#include "ash/keyboard/ui/test/keyboard_test_util.h" #include "ash/keyboard/ui/test/keyboard_test_util.h"
#include "ash/public/cpp/assistant/assistant_state.h" #include "ash/public/cpp/assistant/assistant_state.h"
...@@ -58,6 +59,37 @@ void PressHomeButton() { ...@@ -58,6 +59,37 @@ void PressHomeButton() {
AppListShowSource::kShelfButton, base::TimeTicks::Now()); AppListShowSource::kShelfButton, base::TimeTicks::Now());
} }
// Collects all child views of the given templated type.
// This includes direct and indirect children.
// For this class to work, _ChildView must:
// * Inherit from |views::View|.
// * Have a static variable called |kClassName|.
// * Return |_ChildView::kClassName| from its GetClassName() method.
template <class _ChildView>
class ChildViewCollector {
public:
using Views = std::vector<_ChildView*>;
explicit ChildViewCollector(const views::View* parent) : parent_(parent) {}
Views Get() {
Views result;
for (views::View* child : parent_->children())
Get(child, &result);
return result;
}
private:
void Get(views::View* view, Views* result) {
if (view->GetClassName() == _ChildView::kClassName)
result->push_back(static_cast<_ChildView*>(view));
for (views::View* child : view->children())
Get(child, result);
}
const views::View* parent_;
};
} // namespace } // namespace
AssistantAshTestBase::AssistantAshTestBase() AssistantAshTestBase::AssistantAshTestBase()
...@@ -90,6 +122,9 @@ void AssistantAshTestBase::SetUp() { ...@@ -90,6 +122,9 @@ void AssistantAshTestBase::SetUp() {
test_api_->GetAssistantState()->NotifyFeatureAllowed( test_api_->GetAssistantState()->NotifyFeatureAllowed(
mojom::AssistantAllowedState::ALLOWED); mojom::AssistantAllowedState::ALLOWED);
// Set user consent so the suggestion chips are displayed.
SetConsentStatus(ConsentStatus::kActivityControlAccepted);
// Cache controller. // Cache controller.
controller_ = Shell::Get()->assistant_controller(); controller_ = Shell::Get()->assistant_controller();
DCHECK(controller_); DCHECK(controller_);
...@@ -140,8 +175,7 @@ void AssistantAshTestBase::SetTabletMode(bool enable) { ...@@ -140,8 +175,7 @@ void AssistantAshTestBase::SetTabletMode(bool enable) {
test_api_->SetTabletMode(enable); test_api_->SetTabletMode(enable);
} }
void AssistantAshTestBase::SetConsentStatus( void AssistantAshTestBase::SetConsentStatus(ConsentStatus consent_status) {
chromeos::assistant::prefs::ConsentStatus consent_status) {
test_api_->SetConsentStatus(consent_status); test_api_->SetConsentStatus(consent_status);
} }
...@@ -184,7 +218,7 @@ void AssistantAshTestBase::SendQueryThroughTextField(const std::string& query) { ...@@ -184,7 +218,7 @@ void AssistantAshTestBase::SendQueryThroughTextField(const std::string& query) {
test_api_->SendTextQuery(query); test_api_->SendTextQuery(query);
} }
void AssistantAshTestBase::TapOnAndWait(views::View* view) { void AssistantAshTestBase::TapOnAndWait(const views::View* view) {
CheckCanProcessEvents(view); CheckCanProcessEvents(view);
TapAndWait(GetPointInside(view)); TapAndWait(GetPointInside(view));
} }
...@@ -195,7 +229,10 @@ void AssistantAshTestBase::TapAndWait(gfx::Point position) { ...@@ -195,7 +229,10 @@ void AssistantAshTestBase::TapAndWait(gfx::Point position) {
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
} }
void AssistantAshTestBase::ClickOnAndWait(views::View* view) { void AssistantAshTestBase::ClickOnAndWait(
const views::View* view,
bool check_if_view_can_process_events) {
if (check_if_view_can_process_events)
CheckCanProcessEvents(view); CheckCanProcessEvents(view);
GetEventGenerator()->MoveMouseTo(GetPointInside(view)); GetEventGenerator()->MoveMouseTo(GetPointInside(view));
GetEventGenerator()->ClickLeftButton(); GetEventGenerator()->ClickLeftButton();
...@@ -254,6 +291,16 @@ views::View* AssistantAshTestBase::opt_in_view() { ...@@ -254,6 +291,16 @@ views::View* AssistantAshTestBase::opt_in_view() {
return test_api_->opt_in_view(); return test_api_->opt_in_view();
} }
views::View* AssistantAshTestBase::suggestion_chip_container() {
return test_api_->suggestion_chip_container();
}
std::vector<ash::SuggestionChipView*>
AssistantAshTestBase::GetSuggestionChips() {
const views::View* container = suggestion_chip_container();
return ChildViewCollector<ash::SuggestionChipView>{container}.Get();
}
void AssistantAshTestBase::ShowKeyboard() { void AssistantAshTestBase::ShowKeyboard() {
auto* keyboard_controller = keyboard::KeyboardUIController::Get(); auto* keyboard_controller = keyboard::KeyboardUIController::Get();
keyboard_controller->ShowKeyboard(/*lock=*/false); keyboard_controller->ShowKeyboard(/*lock=*/false);
......
...@@ -31,6 +31,7 @@ class AssistantController; ...@@ -31,6 +31,7 @@ class AssistantController;
class AssistantInteractionController; class AssistantInteractionController;
class AssistantInteractionModel; class AssistantInteractionModel;
class AssistantTestApi; class AssistantTestApi;
class SuggestionChipView;
class TestAssistantClient; class TestAssistantClient;
class TestAssistantService; class TestAssistantService;
class TestAssistantSetup; class TestAssistantSetup;
...@@ -41,6 +42,7 @@ class AssistantAshTestBase : public AshTestBase { ...@@ -41,6 +42,7 @@ class AssistantAshTestBase : public AshTestBase {
public: public:
using AssistantEntryPoint = chromeos::assistant::mojom::AssistantEntryPoint; using AssistantEntryPoint = chromeos::assistant::mojom::AssistantEntryPoint;
using AssistantExitPoint = chromeos::assistant::mojom::AssistantExitPoint; using AssistantExitPoint = chromeos::assistant::mojom::AssistantExitPoint;
using ConsentStatus = chromeos::assistant::prefs::ConsentStatus;
AssistantAshTestBase(); AssistantAshTestBase();
explicit AssistantAshTestBase(base::test::TaskEnvironment::TimeSource time); explicit AssistantAshTestBase(base::test::TaskEnvironment::TimeSource time);
...@@ -66,8 +68,8 @@ class AssistantAshTestBase : public AshTestBase { ...@@ -66,8 +68,8 @@ class AssistantAshTestBase : public AshTestBase {
void SetTabletMode(bool enable); void SetTabletMode(bool enable);
// Changes the user preference controlling the status of user consent. // Change the user preference controlling the status of user consent.
void SetConsentStatus(chromeos::assistant::prefs::ConsentStatus); void SetConsentStatus(ConsentStatus);
// Change the user setting controlling whether the user prefers voice or // Change the user setting controlling whether the user prefers voice or
// keyboard. // keyboard.
...@@ -111,7 +113,7 @@ class AssistantAshTestBase : public AshTestBase { ...@@ -111,7 +113,7 @@ class AssistantAshTestBase : public AshTestBase {
// Simulate the user tapping on the given view. // Simulate the user tapping on the given view.
// Waits for the event to be processed. // Waits for the event to be processed.
void TapOnAndWait(views::View* view); void TapOnAndWait(const views::View* view);
// Simulate the user tapping at the given position. // Simulate the user tapping at the given position.
// Waits for the event to be processed. // Waits for the event to be processed.
...@@ -119,19 +121,20 @@ class AssistantAshTestBase : public AshTestBase { ...@@ -119,19 +121,20 @@ class AssistantAshTestBase : public AshTestBase {
// Simulate a mouse click on the given view. // Simulate a mouse click on the given view.
// Waits for the event to be processed. // Waits for the event to be processed.
void ClickOnAndWait(views::View* view); void ClickOnAndWait(const views::View* view,
bool check_if_view_can_process_events = true);
// Returns the current interaction. Returns |base::nullopt| if no interaction // Return the current interaction. Returns |base::nullopt| if no interaction
// is in progress. // is in progress.
base::Optional<chromeos::assistant::mojom::AssistantInteractionMetadata> base::Optional<chromeos::assistant::mojom::AssistantInteractionMetadata>
current_interaction(); current_interaction();
// Creates a new App window, and activate it. // Create a new App window, and activate it.
// Returns a pointer to the newly created window. // Returns a pointer to the newly created window.
// The window will be destroyed when the test is finished. // The window will be destroyed when the test is finished.
aura::Window* SwitchToNewAppWindow(); aura::Window* SwitchToNewAppWindow();
// Creates a new Widget, and activate it. // Create a new Widget, and activate it.
// Returns a pointer to the newly created widget. // Returns a pointer to the newly created widget.
// The widget will be destroyed when the test is finished. // The widget will be destroyed when the test is finished.
views::Widget* SwitchToNewWidget(); views::Widget* SwitchToNewWidget();
...@@ -155,9 +158,15 @@ class AssistantAshTestBase : public AshTestBase { ...@@ -155,9 +158,15 @@ class AssistantAshTestBase : public AshTestBase {
// Return the button to enable text mode. // Return the button to enable text mode.
views::View* keyboard_input_toggle(); views::View* keyboard_input_toggle();
// Returns the button to launch Assistant onboarding. // Return the button to launch Assistant onboarding.
views::View* opt_in_view(); views::View* opt_in_view();
// Return the container with all the suggestion chips.
views::View* suggestion_chip_container();
// Return the suggestion chips that are currently displayed.
std::vector<ash::SuggestionChipView*> GetSuggestionChips();
// Show/Dismiss the on-screen keyboard. // Show/Dismiss the on-screen keyboard.
void ShowKeyboard(); void ShowKeyboard();
void DismissKeyboard(); void DismissKeyboard();
...@@ -172,9 +181,9 @@ class AssistantAshTestBase : public AshTestBase { ...@@ -172,9 +181,9 @@ class AssistantAshTestBase : public AshTestBase {
AssistantInteractionController* interaction_controller(); AssistantInteractionController* interaction_controller();
const AssistantInteractionModel* interaction_model(); const AssistantInteractionModel* interaction_model();
private:
TestAssistantService* assistant_service(); TestAssistantService* assistant_service();
private:
std::unique_ptr<AssistantTestApi> test_api_; std::unique_ptr<AssistantTestApi> test_api_;
std::unique_ptr<TestAssistantSetup> test_setup_; std::unique_ptr<TestAssistantSetup> test_setup_;
std::unique_ptr<TestAssistantWebViewFactory> test_web_view_factory_; std::unique_ptr<TestAssistantWebViewFactory> test_web_view_factory_;
......
...@@ -41,6 +41,12 @@ MockedAssistantInteraction& MockedAssistantInteraction::WithTextResponse( ...@@ -41,6 +41,12 @@ MockedAssistantInteraction& MockedAssistantInteraction::WithTextResponse(
return *this; return *this;
} }
MockedAssistantInteraction& MockedAssistantInteraction::WithSuggestionChip(
const std::string& text) {
response_->AddSuggestionChip(text);
return *this;
}
MockedAssistantInteraction& MockedAssistantInteraction::WithResolution( MockedAssistantInteraction& MockedAssistantInteraction::WithResolution(
Resolution resolution) { Resolution resolution) {
resolution_ = resolution; resolution_ = resolution;
......
...@@ -29,6 +29,7 @@ class MockedAssistantInteraction { ...@@ -29,6 +29,7 @@ class MockedAssistantInteraction {
MockedAssistantInteraction& WithQuery(const std::string& text_query); MockedAssistantInteraction& WithQuery(const std::string& text_query);
MockedAssistantInteraction& WithTextResponse( MockedAssistantInteraction& WithTextResponse(
const std::string& text_response); const std::string& text_response);
MockedAssistantInteraction& WithSuggestionChip(const std::string& text);
MockedAssistantInteraction& WithResolution(Resolution); MockedAssistantInteraction& WithResolution(Resolution);
private: private:
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <vector> #include <vector>
#include "ash/assistant/assistant_interaction_controller.h" #include "ash/assistant/assistant_interaction_controller.h"
#include "base/unguessable_token.h"
#include "chromeos/services/assistant/public/mojom/assistant.mojom.h" #include "chromeos/services/assistant/public/mojom/assistant.mojom.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -19,6 +20,8 @@ using chromeos::assistant::mojom::AssistantInteractionMetadataPtr; ...@@ -19,6 +20,8 @@ using chromeos::assistant::mojom::AssistantInteractionMetadataPtr;
using chromeos::assistant::mojom::AssistantInteractionResolution; using chromeos::assistant::mojom::AssistantInteractionResolution;
using chromeos::assistant::mojom::AssistantInteractionSubscriber; using chromeos::assistant::mojom::AssistantInteractionSubscriber;
using chromeos::assistant::mojom::AssistantInteractionType; using chromeos::assistant::mojom::AssistantInteractionType;
using chromeos::assistant::mojom::AssistantSuggestion;
using chromeos::assistant::mojom::AssistantSuggestionPtr;
// Subscriber that will ensure the LibAssistant contract is enforced. // Subscriber that will ensure the LibAssistant contract is enforced.
// More specifically, it will ensure that: // More specifically, it will ensure that:
...@@ -197,6 +200,27 @@ class TextResponse : public InteractionResponse::Response { ...@@ -197,6 +200,27 @@ class TextResponse : public InteractionResponse::Response {
DISALLOW_COPY_AND_ASSIGN(TextResponse); DISALLOW_COPY_AND_ASSIGN(TextResponse);
}; };
class SuggestionsResponse : public InteractionResponse::Response {
public:
explicit SuggestionsResponse(const std::string& text) : text_(text) {}
SuggestionsResponse(const SuggestionsResponse&) = delete;
SuggestionsResponse& operator=(const SuggestionsResponse&) = delete;
~SuggestionsResponse() override = default;
void SendTo(chromeos::assistant::mojom::AssistantInteractionSubscriber*
receiver) override {
std::vector<AssistantSuggestionPtr> suggestions;
suggestions.emplace_back(AssistantSuggestion::New());
auto& suggestion = suggestions.back();
suggestion->text = text_;
suggestion->id = base::UnguessableToken::Create();
receiver->OnSuggestionsResponse(std::move(suggestions));
}
private:
std::string text_;
};
class ResolutionResponse : public InteractionResponse::Response { class ResolutionResponse : public InteractionResponse::Response {
public: public:
using Resolution = InteractionResponse::Resolution; using Resolution = InteractionResponse::Resolution;
...@@ -341,6 +365,12 @@ InteractionResponse* InteractionResponse::AddTextResponse( ...@@ -341,6 +365,12 @@ InteractionResponse* InteractionResponse::AddTextResponse(
return this; return this;
} }
InteractionResponse* InteractionResponse::AddSuggestionChip(
const std::string& text) {
AddResponse(std::make_unique<SuggestionsResponse>(text));
return this;
}
InteractionResponse* InteractionResponse::AddResolution(Resolution resolution) { InteractionResponse* InteractionResponse::AddResolution(Resolution resolution) {
AddResponse(std::make_unique<ResolutionResponse>(resolution)); AddResponse(std::make_unique<ResolutionResponse>(resolution));
return this; return this;
......
...@@ -41,6 +41,8 @@ class InteractionResponse { ...@@ -41,6 +41,8 @@ class InteractionResponse {
// A simple textual response. // A simple textual response.
InteractionResponse* AddTextResponse(const std::string& text); InteractionResponse* AddTextResponse(const std::string& text);
// A suggestion chip response.
InteractionResponse* AddSuggestionChip(const std::string& text);
// If used this will cause us to finish the interaction by passing the given // If used this will cause us to finish the interaction by passing the given
// |resolution| to |AssistantInteractionSubscriber::OnInteractionFinished|. // |resolution| to |AssistantInteractionSubscriber::OnInteractionFinished|.
InteractionResponse* AddResolution(Resolution resolution); InteractionResponse* AddResolution(Resolution resolution);
......
...@@ -238,8 +238,8 @@ void AnimatedContainerView::AddElementAnimatorAndAnimateInView( ...@@ -238,8 +238,8 @@ void AnimatedContainerView::AddElementAnimatorAndAnimateInView(
DCHECK_EQ(animator->view()->parent(), content_view()); DCHECK_EQ(animator->view()->parent(), content_view());
animators_.push_back(std::move(animator)); animators_.push_back(std::move(animator));
// We don't allow processing of events while animating. // We don't allow interactions while animating.
set_can_process_events_within_subtree(false); DisableInteractions();
auto* animation_observer = new ui::CallbackLayerAnimationObserver( auto* animation_observer = new ui::CallbackLayerAnimationObserver(
/*animation_ended_callback=*/base::BindRepeating( /*animation_ended_callback=*/base::BindRepeating(
...@@ -260,9 +260,9 @@ void AnimatedContainerView::FadeOutViews() { ...@@ -260,9 +260,9 @@ void AnimatedContainerView::FadeOutViews() {
fade_out_in_progress_ = true; fade_out_in_progress_ = true;
// We don't allow processing of events while waiting for the next query // We don't allow interactions while waiting for the next query response. The
// response. The contents will be faded out, so it should not be interactive. // contents will be faded out, so it should not be interactive.
set_can_process_events_within_subtree(false); DisableInteractions();
auto* animation_observer = new ui::CallbackLayerAnimationObserver( auto* animation_observer = new ui::CallbackLayerAnimationObserver(
/*animation_ended_callback=*/base::BindRepeating( /*animation_ended_callback=*/base::BindRepeating(
...@@ -276,6 +276,11 @@ void AnimatedContainerView::FadeOutViews() { ...@@ -276,6 +276,11 @@ void AnimatedContainerView::FadeOutViews() {
animation_observer->SetActive(); animation_observer->SetActive();
} }
void AnimatedContainerView::SetInteractionsEnabled(bool enabled) {
for (const auto& animator : animators_)
animator->view()->SetEnabled(enabled);
}
bool AnimatedContainerView::AnimateInObserverCallback( bool AnimatedContainerView::AnimateInObserverCallback(
const base::WeakPtr<AnimatedContainerView>& weak_ptr, const base::WeakPtr<AnimatedContainerView>& weak_ptr,
const ui::CallbackLayerAnimationObserver& observer) { const ui::CallbackLayerAnimationObserver& observer) {
...@@ -295,7 +300,7 @@ bool AnimatedContainerView::AnimateInObserverCallback( ...@@ -295,7 +300,7 @@ bool AnimatedContainerView::AnimateInObserverCallback(
// off prior to this animation finishing. Once all animations have completed // off prior to this animation finishing. Once all animations have completed
// interactivity will be restored and derivate classes notified. // interactivity will be restored and derivate classes notified.
if (!weak_ptr->IsAnimatingViews()) { if (!weak_ptr->IsAnimatingViews()) {
weak_ptr->set_can_process_events_within_subtree(true); weak_ptr->EnableInteractions();
weak_ptr->OnAllViewsAnimatedIn(); weak_ptr->OnAllViewsAnimatedIn();
} }
......
...@@ -117,6 +117,10 @@ class COMPONENT_EXPORT(ASSISTANT_UI) AnimatedContainerView ...@@ -117,6 +117,10 @@ class COMPONENT_EXPORT(ASSISTANT_UI) AnimatedContainerView
void AddElementAnimatorAndAnimateInView(std::unique_ptr<ElementAnimator>); void AddElementAnimatorAndAnimateInView(std::unique_ptr<ElementAnimator>);
void FadeOutViews(); void FadeOutViews();
void EnableInteractions() { SetInteractionsEnabled(true); }
void DisableInteractions() { SetInteractionsEnabled(false); }
void SetInteractionsEnabled(bool enabled);
static bool AnimateInObserverCallback( static bool AnimateInObserverCallback(
const base::WeakPtr<AnimatedContainerView>& weak_ptr, const base::WeakPtr<AnimatedContainerView>& weak_ptr,
const ui::CallbackLayerAnimationObserver& observer); const ui::CallbackLayerAnimationObserver& observer);
......
...@@ -39,6 +39,9 @@ constexpr int kPreferredHeightDip = 32; ...@@ -39,6 +39,9 @@ constexpr int kPreferredHeightDip = 32;
// SuggestionChipView ---------------------------------------------------------- // SuggestionChipView ----------------------------------------------------------
// static
constexpr char SuggestionChipView::kClassName[];
SuggestionChipView::SuggestionChipView(AssistantViewDelegate* delegate, SuggestionChipView::SuggestionChipView(AssistantViewDelegate* delegate,
const AssistantSuggestion* suggestion, const AssistantSuggestion* suggestion,
views::ButtonListener* listener) views::ButtonListener* listener)
...@@ -48,6 +51,10 @@ SuggestionChipView::SuggestionChipView(AssistantViewDelegate* delegate, ...@@ -48,6 +51,10 @@ SuggestionChipView::SuggestionChipView(AssistantViewDelegate* delegate,
SuggestionChipView::~SuggestionChipView() = default; SuggestionChipView::~SuggestionChipView() = default;
const char* SuggestionChipView::GetClassName() const {
return kClassName;
}
gfx::Size SuggestionChipView::CalculatePreferredSize() const { gfx::Size SuggestionChipView::CalculatePreferredSize() const {
const int preferred_width = views::View::CalculatePreferredSize().width(); const int preferred_width = views::View::CalculatePreferredSize().width();
return gfx::Size(preferred_width, GetHeightForWidth(preferred_width)); return gfx::Size(preferred_width, GetHeightForWidth(preferred_width));
......
...@@ -25,12 +25,15 @@ class COMPONENT_EXPORT(ASSISTANT_UI) SuggestionChipView : public views::Button { ...@@ -25,12 +25,15 @@ class COMPONENT_EXPORT(ASSISTANT_UI) SuggestionChipView : public views::Button {
public: public:
using AssistantSuggestion = chromeos::assistant::mojom::AssistantSuggestion; using AssistantSuggestion = chromeos::assistant::mojom::AssistantSuggestion;
static constexpr char kClassName[] = "SuggestionChipView";
SuggestionChipView(AssistantViewDelegate* delegate, SuggestionChipView(AssistantViewDelegate* delegate,
const AssistantSuggestion* suggestion, const AssistantSuggestion* suggestion,
views::ButtonListener* listener); views::ButtonListener* listener);
~SuggestionChipView() override; ~SuggestionChipView() override;
// views::View: // views::View:
const char* GetClassName() const override;
gfx::Size CalculatePreferredSize() const override; gfx::Size CalculatePreferredSize() const override;
int GetHeightForWidth(int width) const override; int GetHeightForWidth(int width) const override;
void ChildVisibilityChanged(views::View* child) override; void ChildVisibilityChanged(views::View* child) override;
......
...@@ -105,6 +105,10 @@ class ASH_EXPORT AssistantTestApi { ...@@ -105,6 +105,10 @@ class ASH_EXPORT AssistantTestApi {
// Can only be used after the Assistant UI has been shown at least once. // Can only be used after the Assistant UI has been shown at least once.
virtual views::View* opt_in_view() = 0; virtual views::View* opt_in_view() = 0;
// Returns the view containing the suggestion chips.
// Can only be used after the Assistant UI has been shown at least once.
virtual views::View* suggestion_chip_container() = 0;
// Returns the window containing the Assistant UI. // Returns the window containing the Assistant UI.
// Note that this window is shared for all components of the |AppList|. // Note that this window is shared for all components of the |AppList|.
// Can only be used after the Assistant UI has been shown at least once. // Can only be used after the Assistant UI has been shown at least once.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment