Commit a41583b2 authored by David Black's avatar David Black Committed by Commit Bot

Improves ChromeVox behavior in Assistant.

This CL:
- Stops ChromeVox from speaking when the mic is opened for Assistant.
- No longer focuses ActionView when switching to voice input modality.

Bug: b:116139555, b:116143886
Change-Id: Ibdd316f6ed83b856adeaae7fd3ddd3796951348f
Reviewed-on: https://chromium-review.googlesource.com/c/1263564
Commit-Queue: David Black <dmblack@google.com>
Reviewed-by: default avatarXiaohui Chen <xiaohuic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#602070}
parent a4bf09f9
......@@ -198,6 +198,8 @@ component("ash") {
"assistant/ui/assistant_container_view_animator.h",
"assistant/ui/assistant_container_view_animator_legacy_impl.cc",
"assistant/ui/assistant_container_view_animator_legacy_impl.h",
"assistant/ui/assistant_container_view_focus_traversable.cc",
"assistant/ui/assistant_container_view_focus_traversable.h",
"assistant/ui/assistant_main_view.cc",
"assistant/ui/assistant_main_view.h",
"assistant/ui/assistant_mini_view.cc",
......
......@@ -6,6 +6,7 @@
#include <utility>
#include "ash/accessibility/accessibility_controller.h"
#include "ash/assistant/assistant_controller.h"
#include "ash/assistant/assistant_screen_context_controller.h"
#include "ash/assistant/assistant_ui_controller.h"
......@@ -226,6 +227,12 @@ void AssistantInteractionController::OnInputModalityChanged(
StopActiveInteraction(false);
}
void AssistantInteractionController::OnMicStateChanged(MicState mic_state) {
// We should stop ChromeVox from speaking when opening the mic.
if (mic_state == MicState::kOpen)
Shell::Get()->accessibility_controller()->SilenceSpokenFeedback();
}
void AssistantInteractionController::OnResponseChanged(
const std::shared_ptr<AssistantResponse>& response) {
assistant::util::IncrementAssistantQueryCountForEntryPoint(
......
......@@ -68,6 +68,7 @@ class AssistantInteractionController
// AssistantInteractionModelObserver:
void OnInteractionStateChanged(InteractionState interaction_state) override;
void OnInputModalityChanged(InputModality input_modality) override;
void OnMicStateChanged(MicState mic_state) override;
void OnResponseChanged(
const std::shared_ptr<AssistantResponse>& response) override;
......
......@@ -147,7 +147,8 @@ AssistantContainerView::AssistantContainerView(
AssistantController* assistant_controller)
: assistant_controller_(assistant_controller),
animator_(
AssistantContainerViewAnimator::Create(assistant_controller_, this)) {
AssistantContainerViewAnimator::Create(assistant_controller_, this)),
focus_traversable_(this) {
UpdateAnchor();
set_accept_events(true);
......@@ -197,14 +198,29 @@ void AssistantContainerView::AddedToWidget() {
std::make_unique<AssistantContainerEventTargeter>());
}
ax::mojom::Role AssistantContainerView::GetAccessibleWindowRole() const {
return ax::mojom::Role::kWindow;
}
int AssistantContainerView::GetDialogButtons() const {
return ui::DIALOG_BUTTON_NONE;
}
views::FocusTraversable* AssistantContainerView::GetFocusTraversable() {
return &focus_traversable_;
}
void AssistantContainerView::ChildPreferredSizeChanged(views::View* child) {
PreferredSizeChanged();
}
void AssistantContainerView::ViewHierarchyChanged(
const ViewHierarchyChangedDetails& details) {
// Do nothing. We override this method to prevent a super class implementation
// from taking effect which would otherwise cause ChromeVox to read the entire
// Assistant view hierarchy.
}
void AssistantContainerView::SizeToContents() {
// We override this method to increase its visibility.
views::BubbleDialogDelegateView::SizeToContents();
......@@ -304,6 +320,24 @@ void AssistantContainerView::OnUsableWorkAreaChanged(
PreferredSizeChanged();
}
views::View* AssistantContainerView::FindFirstFocusableView() {
if (!GetWidget() || !GetWidget()->IsActive())
return nullptr;
switch (assistant_controller_->ui_controller()->model()->ui_mode()) {
case AssistantUiMode::kMainUi:
// AssistantMainView will sometimes explicitly specify a view to be
// focused first. Other times it may defer to views::FocusSearch.
return assistant_main_view_
? assistant_main_view_->FindFirstFocusableView()
: nullptr;
case AssistantUiMode::kMiniUi:
case AssistantUiMode::kWebUi:
// Default views::FocusSearch behavior is acceptable.
return nullptr;
}
}
SkColor AssistantContainerView::GetBackgroundColor() const {
return kBackgroundColor;
}
......
......@@ -8,6 +8,7 @@
#include <memory>
#include "ash/assistant/model/assistant_ui_model_observer.h"
#include "ash/assistant/ui/assistant_container_view_focus_traversable.h"
#include "base/macros.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
......@@ -37,8 +38,12 @@ class AssistantContainerView : public views::BubbleDialogDelegateView,
// views::BubbleDialogDelegateView:
const char* GetClassName() const override;
void AddedToWidget() override;
ax::mojom::Role GetAccessibleWindowRole() const override;
int GetDialogButtons() const override;
views::FocusTraversable* GetFocusTraversable() override;
void ChildPreferredSizeChanged(views::View* child) override;
void ViewHierarchyChanged(
const ViewHierarchyChangedDetails& details) override;
void SizeToContents() override;
void OnBeforeBubbleWidgetInit(views::Widget::InitParams* params,
views::Widget* widget) const override;
......@@ -49,6 +54,9 @@ class AssistantContainerView : public views::BubbleDialogDelegateView,
void OnUiModeChanged(AssistantUiMode ui_mode) override;
void OnUsableWorkAreaChanged(const gfx::Rect& usable_work_area) override;
// Returns the first focusable view or nullptr to defer to views::FocusSearch.
views::View* FindFirstFocusableView();
// Returns background color.
SkColor GetBackgroundColor() const;
......@@ -70,6 +78,7 @@ class AssistantContainerView : public views::BubbleDialogDelegateView,
AssistantWebView* assistant_web_view_; // Owned by view hierarchy.
std::unique_ptr<AssistantContainerViewAnimator> animator_;
AssistantContainerViewFocusTraversable focus_traversable_;
DISALLOW_COPY_AND_ASSIGN(AssistantContainerView);
};
......
// 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 "ash/assistant/ui/assistant_container_view_focus_traversable.h"
#include "ash/assistant/ui/assistant_container_view.h"
namespace ash {
// AssistantContainerViewFocusSearch -------------------------------------------
AssistantContainerViewFocusSearch::AssistantContainerViewFocusSearch(
AssistantContainerView* assistant_container_view)
: views::FocusSearch(/*root=*/assistant_container_view,
/*cycle=*/true,
/*accessibility_mode=*/false),
assistant_container_view_(assistant_container_view) {}
AssistantContainerViewFocusSearch::~AssistantContainerViewFocusSearch() =
default;
views::View* AssistantContainerViewFocusSearch::FindNextFocusableView(
views::View* starting_from,
SearchDirection search_direction,
TraversalDirection traversal_direction,
StartingViewPolicy check_starting_view,
AnchoredDialogPolicy can_go_into_anchored_dialog,
views::FocusTraversable** focus_traversable,
views::View** focus_traversable_view) {
views::FocusManager* focus_manager =
assistant_container_view_->GetFocusManager();
// If there is no currently focused view we'll give AssistantContainerView
// an opportunity to explicitly specified which view to focus first.
views::View* next_focusable_view = nullptr;
if (focus_manager && !focus_manager->GetFocusedView())
next_focusable_view = assistant_container_view_->FindFirstFocusableView();
// When we are not explicitly overriding the next focusable view we defer
// back to views::FocusSearch's default behaviour.
return next_focusable_view
? next_focusable_view
: views::FocusSearch::FindNextFocusableView(
starting_from, search_direction, traversal_direction,
check_starting_view, can_go_into_anchored_dialog,
focus_traversable, focus_traversable_view);
}
// AssistantContainerViewFocusTraversable --------------------------------------
AssistantContainerViewFocusTraversable::AssistantContainerViewFocusTraversable(
AssistantContainerView* assistant_container_view)
: assistant_container_view_(assistant_container_view),
focus_search_(assistant_container_view) {}
AssistantContainerViewFocusTraversable::
~AssistantContainerViewFocusTraversable() = default;
views::FocusSearch* AssistantContainerViewFocusTraversable::GetFocusSearch() {
return &focus_search_;
}
views::FocusTraversable*
AssistantContainerViewFocusTraversable::GetFocusTraversableParent() {
return nullptr;
}
views::View*
AssistantContainerViewFocusTraversable::GetFocusTraversableParentView() {
return assistant_container_view_;
}
} // namespace ash
// 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 ASH_ASSISTANT_UI_ASSISTANT_CONTAINER_VIEW_FOCUS_TRAVERSABLE_H_
#define ASH_ASSISTANT_UI_ASSISTANT_CONTAINER_VIEW_FOCUS_TRAVERSABLE_H_
#include <memory>
#include "base/macros.h"
#include "ui/views/focus/focus_manager.h"
#include "ui/views/focus/focus_search.h"
namespace ash {
class AssistantContainerView;
// AssistantContainerViewFocusSearch -------------------------------------------
// AssistantContainerViewFocusSearch is an implementation of views::FocusSearch
// belonging to AssistantContainerViewFocusTraversable. When there is no
// currently focused view, it delegates to AssistantContainerView to find the
// first focusable view for the given UI state.
class AssistantContainerViewFocusSearch : public views::FocusSearch {
public:
explicit AssistantContainerViewFocusSearch(
AssistantContainerView* assistant_container_view);
~AssistantContainerViewFocusSearch() override;
// views::FocusSearch:
views::View* FindNextFocusableView(
views::View* starting_from,
SearchDirection search_direction,
TraversalDirection traversal_direction,
StartingViewPolicy check_starting_view,
AnchoredDialogPolicy can_go_into_anchored_dialog,
views::FocusTraversable** focus_traversable,
views::View** focus_traversable_view) override;
private:
AssistantContainerView* const assistant_container_view_;
DISALLOW_COPY_AND_ASSIGN(AssistantContainerViewFocusSearch);
};
// AssistantContainerViewFocusTraversable --------------------------------------
// AssistantContainerViewFocusTraversable is an implementation of
// views::FocusTraversable belonging to AssistantContainerView. It wraps an
// AssistantContainerViewFocusSearch instance which allows us to override focus
// behavior as needed.
class AssistantContainerViewFocusTraversable : public views::FocusTraversable {
public:
explicit AssistantContainerViewFocusTraversable(
AssistantContainerView* assistant_container_view);
~AssistantContainerViewFocusTraversable() override;
// views::FocusTraversable:
views::FocusSearch* GetFocusSearch() override;
views::FocusTraversable* GetFocusTraversableParent() override;
views::View* GetFocusTraversableParentView() override;
private:
AssistantContainerView* const assistant_container_view_;
AssistantContainerViewFocusSearch focus_search_;
DISALLOW_COPY_AND_ASSIGN(AssistantContainerViewFocusTraversable);
};
} // namespace ash
#endif // ASH_ASSISTANT_UI_ASSISTANT_CONTAINER_VIEW_FOCUS_TRAVERSABLE_H_
......@@ -114,6 +114,12 @@ void AssistantMainView::ChildVisibilityChanged(views::View* child) {
PreferredSizeChanged();
}
views::View* AssistantMainView::FindFirstFocusableView() {
// In those instances in which we want to override views::FocusSearch
// behavior, DialogPlate will identify the first focusable view.
return dialog_plate_->FindFirstFocusableView();
}
void AssistantMainView::InitLayout() {
views::BoxLayout* layout_manager =
SetLayoutManager(std::make_unique<views::BoxLayout>(
......
......@@ -35,6 +35,9 @@ class AssistantMainView : public views::View, public AssistantUiModelObserver {
AssistantVisibility old_visibility,
AssistantSource source) override;
// Returns the first focusable view or nullptr to defer to views::FocusSearch.
views::View* FindFirstFocusableView();
private:
void InitLayout();
......
......@@ -234,6 +234,23 @@ void DialogPlate::RequestFocus() {
->input_modality());
}
views::View* DialogPlate::FindFirstFocusableView() {
InputModality input_modality = assistant_controller_->interaction_controller()
->model()
->input_modality();
// The first focusable view depends entirely on current input modality.
switch (input_modality) {
case InputModality::kKeyboard:
return textfield_;
case InputModality::kVoice:
return animated_voice_input_toggle_;
case InputModality::kStylus:
// Default views::FocusSearch behavior is acceptable.
return nullptr;
}
}
void DialogPlate::InitLayout() {
constexpr int kRightPaddingDip = 8;
......@@ -385,7 +402,6 @@ bool DialogPlate::OnAnimationEnded(
InputModality input_modality = assistant_controller_->interaction_controller()
->model()
->input_modality();
SetFocus(input_modality);
switch (input_modality) {
case InputModality::kKeyboard:
......@@ -401,6 +417,8 @@ bool DialogPlate::OnAnimationEnded(
break;
}
SetFocus(input_modality);
// We return false so that the animation observer will not destroy itself.
return false;
}
......@@ -411,10 +429,17 @@ void DialogPlate::SetFocus(InputModality input_modality) {
textfield_->RequestFocus();
break;
case InputModality::kVoice:
animated_voice_input_toggle_->RequestFocus();
break;
case InputModality::kStylus:
// No action necessary.
// When not using |kKeyboard| input modality we need to explicitly clear
// focus if the focused view is |textfield_| or |voice_input_toggle_| to
// prevent it from being read by ChromeVox. Clearing focus also allows
// AssistantContainerView's focus traversal to be reset.
views::FocusManager* focus_manager = GetFocusManager();
if (focus_manager &&
(focus_manager->GetFocusedView() == textfield_ ||
focus_manager->GetFocusedView() == voice_input_toggle_)) {
focus_manager->ClearFocus();
}
break;
}
}
......
......@@ -93,6 +93,9 @@ class DialogPlate : public views::View,
AssistantVisibility old_visibility,
AssistantSource source) override;
// Returns the first focusable view or nullptr to defer to views::FocusSearch.
views::View* FindFirstFocusableView();
private:
void InitLayout();
void InitKeyboardLayoutContainer();
......
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