Commit 418376ae authored by David Black's avatar David Black Committed by Commit Bot

Improve/simplify stop interaction behavior.

Changes include:
- Stopping interaction on widget close (fixes ESC key bug).
- Stopping interaction on input modality change (if not voice).

Bug: b:79441527
Change-Id: I99e3de5ac8967e79a81bfbe1733da99df52ed03f
Reviewed-on: https://chromium-review.googlesource.com/1052430
Commit-Queue: David Black <dmblack@google.com>
Reviewed-by: default avatarXiaohui Chen <xiaohuic@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#558779}
parent e48c4e0a
...@@ -1850,6 +1850,7 @@ test("ash_unittests") { ...@@ -1850,6 +1850,7 @@ test("ash_unittests") {
"//chromeos:power_manager_proto", "//chromeos:power_manager_proto",
"//chromeos:test_support", "//chromeos:test_support",
"//chromeos:test_support_without_gmock", "//chromeos:test_support_without_gmock",
"//chromeos/services/assistant:test_support",
"//chromeos/services/multidevice_setup/public/mojom", "//chromeos/services/multidevice_setup/public/mojom",
"//components/account_id", "//components/account_id",
"//components/password_manager/core/browser:hash_password_manager", "//components/password_manager/core/browser:hash_password_manager",
......
...@@ -79,6 +79,7 @@ include_rules = [ ...@@ -79,6 +79,7 @@ include_rules = [
# TODO(jamescook): Eliminate this. http://crbug.com/644355 # TODO(jamescook): Eliminate this. http://crbug.com/644355
"+chromeos/network", "+chromeos/network",
"+chromeos/services/assistant/public" , "+chromeos/services/assistant/public" ,
"+chromeos/services/assistant/test_support",
"+chromeos/services/multidevice_setup/public/mojom", "+chromeos/services/multidevice_setup/public/mojom",
# TODO(jamescook): Eliminate this. http://crbug.com/644361 # TODO(jamescook): Eliminate this. http://crbug.com/644361
"+chromeos/settings/timezone_settings.h", "+chromeos/settings/timezone_settings.h",
......
...@@ -93,19 +93,11 @@ void AssistantController::RemoveInteractionModelObserver( ...@@ -93,19 +93,11 @@ void AssistantController::RemoveInteractionModelObserver(
} }
void AssistantController::StartInteraction() { void AssistantController::StartInteraction() {
// TODO(dmblack): Instruct underlying service to start listening if current OnInteractionStarted();
// input modality is VOICE.
if (assistant_interaction_model_.interaction_state() ==
InteractionState::kInactive) {
OnInteractionStarted();
}
} }
void AssistantController::StopInteraction() { void AssistantController::StopInteraction() {
if (assistant_interaction_model_.interaction_state() != assistant_interaction_model_.SetInteractionState(InteractionState::kInactive);
InteractionState::kInactive) {
OnInteractionDismissed();
}
} }
void AssistantController::ToggleInteraction() { void AssistantController::ToggleInteraction() {
...@@ -119,13 +111,16 @@ void AssistantController::ToggleInteraction() { ...@@ -119,13 +111,16 @@ void AssistantController::ToggleInteraction() {
void AssistantController::OnInteractionStateChanged( void AssistantController::OnInteractionStateChanged(
InteractionState interaction_state) { InteractionState interaction_state) {
if (interaction_state == InteractionState::kInactive) { if (interaction_state == InteractionState::kActive)
assistant_interaction_model_.ClearInteraction(); return;
// TODO(dmblack): Input modality should default back to the user's // When the user-facing interaction is dismissed, we instruct the service to
// preferred input modality. // terminate any listening, speaking, or query in flight.
assistant_interaction_model_.SetInputModality(InputModality::kVoice); DCHECK(assistant_);
} assistant_->StopActiveInteraction();
assistant_interaction_model_.ClearInteraction();
assistant_interaction_model_.SetInputModality(InputModality::kVoice);
} }
void AssistantController::OnHighlighterEnabledChanged( void AssistantController::OnHighlighterEnabledChanged(
...@@ -139,21 +134,26 @@ void AssistantController::OnHighlighterEnabledChanged( ...@@ -139,21 +134,26 @@ void AssistantController::OnHighlighterEnabledChanged(
} }
} }
void AssistantController::OnInputModalityChanged(InputModality input_modality) {
if (input_modality == InputModality::kVoice)
return;
// When switching to a non-voice input modality we instruct the underlying
// service to terminate any listening, speaking, or in flight query. We do not
// do this when switching to voice input modality because initiation of a
// voice interaction will automatically interrupt any pre-existing activity.
// Stopping the active interaction here for voice input modality would
// actually have the undesired effect of stopping the voice interaction.
DCHECK(assistant_);
assistant_->StopActiveInteraction();
}
void AssistantController::OnInteractionStarted() { void AssistantController::OnInteractionStarted() {
assistant_interaction_model_.SetInteractionState(InteractionState::kActive); assistant_interaction_model_.SetInteractionState(InteractionState::kActive);
} }
void AssistantController::OnInteractionFinished( void AssistantController::OnInteractionFinished(
AssistantInteractionResolution resolution) {} chromeos::assistant::mojom::AssistantInteractionResolution resolution) {}
void AssistantController::OnInteractionDismissed() {
assistant_interaction_model_.SetInteractionState(InteractionState::kInactive);
// When the user-facing interaction is dismissed, we instruct the service to
// terminate any listening, speaking, or query in flight.
DCHECK(assistant_);
assistant_->StopActiveInteraction();
}
void AssistantController::OnDialogPlateContentsChanged( void AssistantController::OnDialogPlateContentsChanged(
const std::string& text) { const std::string& text) {
...@@ -162,16 +162,6 @@ void AssistantController::OnDialogPlateContentsChanged( ...@@ -162,16 +162,6 @@ void AssistantController::OnDialogPlateContentsChanged(
// voice so that we will show the mic icon in the UI. // voice so that we will show the mic icon in the UI.
assistant_interaction_model_.SetInputModality(InputModality::kVoice); assistant_interaction_model_.SetInputModality(InputModality::kVoice);
} else { } else {
// If switching to keyboard modality from voice, we instruct the service to
// terminate any listening, speaking, or query in flight.
// TODO(dmblack): We probably want this same logic when switching to any
// modality from voice. Handle this instead in OnInputModalityChanged, but
// we will need to add a previous modality parameter to that API.
if (assistant_interaction_model_.input_modality() ==
InputModality::kVoice) {
DCHECK(assistant_);
assistant_->StopActiveInteraction();
}
assistant_interaction_model_.SetInputModality(InputModality::kKeyboard); assistant_interaction_model_.SetInputModality(InputModality::kKeyboard);
assistant_interaction_model_.SetMicState(MicState::kClosed); assistant_interaction_model_.SetMicState(MicState::kClosed);
} }
......
...@@ -86,6 +86,7 @@ class AssistantController ...@@ -86,6 +86,7 @@ class AssistantController
void OnSuggestionChipPressed(int id); void OnSuggestionChipPressed(int id);
// AssistantInteractionModelObserver: // AssistantInteractionModelObserver:
void OnInputModalityChanged(InputModality input_modality) override;
void OnInteractionStateChanged(InteractionState interaction_state) override; void OnInteractionStateChanged(InteractionState interaction_state) override;
// HighlighterController::Observer: // HighlighterController::Observer:
...@@ -115,8 +116,6 @@ class AssistantController ...@@ -115,8 +116,6 @@ class AssistantController
mojom::AssistantCardRendererPtr assistant_card_renderer) override; mojom::AssistantCardRendererPtr assistant_card_renderer) override;
private: private:
void OnInteractionDismissed();
mojo::Binding<mojom::AssistantController> assistant_controller_binding_; mojo::Binding<mojom::AssistantController> assistant_controller_binding_;
mojo::Binding<chromeos::assistant::mojom::AssistantEventSubscriber> mojo::Binding<chromeos::assistant::mojom::AssistantEventSubscriber>
assistant_event_subscriber_binding_; assistant_event_subscriber_binding_;
......
...@@ -4,12 +4,16 @@ ...@@ -4,12 +4,16 @@
#include "ash/assistant/assistant_controller.h" #include "ash/assistant/assistant_controller.h"
#include <memory>
#include "ash/highlighter/highlighter_controller.h" #include "ash/highlighter/highlighter_controller.h"
#include "ash/shell.h" #include "ash/shell.h"
#include "ash/test/ash_test_base.h" #include "ash/test/ash_test_base.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/test/scoped_feature_list.h" #include "base/test/scoped_feature_list.h"
#include "chromeos/chromeos_switches.h" #include "chromeos/chromeos_switches.h"
#include "chromeos/services/assistant/test_support/mock_assistant.h"
#include "mojo/public/cpp/bindings/binding.h"
namespace ash { namespace ash {
...@@ -25,6 +29,15 @@ class AssistantControllerTest : public AshTestBase { ...@@ -25,6 +29,15 @@ class AssistantControllerTest : public AshTestBase {
AshTestBase::SetUp(); AshTestBase::SetUp();
controller_ = Shell::Get()->assistant_controller(); controller_ = Shell::Get()->assistant_controller();
DCHECK(controller_); DCHECK(controller_);
// Mock Assistant.
assistant_ = std::make_unique<chromeos::assistant::MockAssistant>();
assistant_binding_ =
std::make_unique<mojo::Binding<chromeos::assistant::mojom::Assistant>>(
assistant_.get());
chromeos::assistant::mojom::AssistantPtr assistant;
assistant_binding_->Bind(mojo::MakeRequest(&assistant));
controller_->SetAssistant(std::move(assistant));
} }
const AssistantInteractionModel* interaction_model() { const AssistantInteractionModel* interaction_model() {
...@@ -35,6 +48,10 @@ class AssistantControllerTest : public AshTestBase { ...@@ -35,6 +48,10 @@ class AssistantControllerTest : public AshTestBase {
base::test::ScopedFeatureList scoped_feature_list_; base::test::ScopedFeatureList scoped_feature_list_;
AssistantController* controller_ = nullptr; AssistantController* controller_ = nullptr;
std::unique_ptr<chromeos::assistant::MockAssistant> assistant_;
std::unique_ptr<mojo::Binding<chromeos::assistant::mojom::Assistant>>
assistant_binding_;
DISALLOW_COPY_AND_ASSIGN(AssistantControllerTest); DISALLOW_COPY_AND_ASSIGN(AssistantControllerTest);
}; };
......
...@@ -11,10 +11,7 @@ ...@@ -11,10 +11,7 @@
namespace ash { namespace ash {
AssistantInteractionModel::AssistantInteractionModel() AssistantInteractionModel::AssistantInteractionModel()
: query_(std::make_unique<AssistantEmptyQuery>()) { : query_(std::make_unique<AssistantEmptyQuery>()) {}
// TODO(dmblack): Default input modality should be read from user preferences.
input_modality_ = InputModality::kVoice;
}
AssistantInteractionModel::~AssistantInteractionModel() = default; AssistantInteractionModel::~AssistantInteractionModel() = default;
......
...@@ -117,7 +117,7 @@ class AssistantInteractionModel { ...@@ -117,7 +117,7 @@ class AssistantInteractionModel {
void NotifySuggestionsCleared(); void NotifySuggestionsCleared();
InteractionState interaction_state_ = InteractionState::kInactive; InteractionState interaction_state_ = InteractionState::kInactive;
InputModality input_modality_; InputModality input_modality_ = InputModality::kVoice;
MicState mic_state_ = MicState::kClosed; MicState mic_state_ = MicState::kClosed;
std::unique_ptr<AssistantQuery> query_; std::unique_ptr<AssistantQuery> query_;
std::vector<AssistantSuggestionPtr> suggestions_; std::vector<AssistantSuggestionPtr> suggestions_;
......
...@@ -127,6 +127,12 @@ void AssistantBubble::OnWidgetActivationChanged(views::Widget* widget, ...@@ -127,6 +127,12 @@ void AssistantBubble::OnWidgetActivationChanged(views::Widget* widget,
} }
void AssistantBubble::OnWidgetDestroying(views::Widget* widget) { void AssistantBubble::OnWidgetDestroying(views::Widget* widget) {
// We need to be sure that the Assistant interaction is stopped when the
// widget is closed. Special cases, such as closing the widget via the |ESC|
// key might otherwise go unhandled, causing inconsistencies between the
// widget visibility state and the underlying interaction model state.
assistant_controller_->StopInteraction();
container_view_->GetWidget()->RemoveObserver(this); container_view_->GetWidget()->RemoveObserver(this);
container_view_ = nullptr; container_view_ = nullptr;
} }
......
...@@ -120,3 +120,16 @@ catalog("tests_catalog") { ...@@ -120,3 +120,16 @@ catalog("tests_catalog") {
testonly = true testonly = true
embedded_services = [ ":unittest_manifest" ] embedded_services = [ ":unittest_manifest" ]
} }
static_library("test_support") {
testonly = true
sources = [
"test_support/mock_assistant.cc",
"test_support/mock_assistant.h",
]
deps = [
"//base",
"//chromeos/services/assistant/public/mojom",
"//testing/gmock",
]
}
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/services/assistant/test_support/mock_assistant.h"
namespace chromeos {
namespace assistant {
MockAssistant::MockAssistant() = default;
MockAssistant::~MockAssistant() = default;
} // namespace assistant
} // namespace chromeos
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROMEOS_SERVICES_ASSISTANT_TEST_SUPPORT_MOCK_ASSISTANT_H_
#define CHROMEOS_SERVICES_ASSISTANT_TEST_SUPPORT_MOCK_ASSISTANT_H_
#include "base/macros.h"
#include "chromeos/services/assistant/public/mojom/assistant.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace chromeos {
namespace assistant {
class MockAssistant : public mojom::Assistant {
public:
MockAssistant();
~MockAssistant() override;
MOCK_METHOD0(StartVoiceInteraction, void());
MOCK_METHOD0(StopActiveInteraction, void());
MOCK_METHOD1(SendTextQuery, void(const std::string&));
MOCK_METHOD1(AddAssistantEventSubscriber,
void(chromeos::assistant::mojom::AssistantEventSubscriberPtr));
private:
DISALLOW_COPY_AND_ASSIGN(MockAssistant);
};
} // namespace assistant
} // namespace chromeos
#endif // CHROMEOS_SERVICES_ASSISTANT_TEST_SUPPORT_MOCK_ASSISTANT_H_
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