Commit f6e3fa0f authored by Joel Riley's avatar Joel Riley Committed by Chromium LUCI CQ

Support changing speech rate in Select-to-speak.

Also update floating panel UI to match visual spec: go/enhanced-sts-spec

AX-Relnotes: N/A

Bug: 1143825
Change-Id: I0d5cf4b5c026e7eb7b5ccc6944a77d9247cab41e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2577882
Commit-Queue: Joel Riley <joelriley@google.com>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#835846}
parent 00e84568
...@@ -897,6 +897,7 @@ component("ash") { ...@@ -897,6 +897,7 @@ component("ash") {
"system/accessibility/floating_menu_button.h", "system/accessibility/floating_menu_button.h",
"system/accessibility/floating_menu_utils.cc", "system/accessibility/floating_menu_utils.cc",
"system/accessibility/floating_menu_utils.h", "system/accessibility/floating_menu_utils.h",
"system/accessibility/select_to_speak_constants.h",
"system/accessibility/select_to_speak_menu_bubble_controller.cc", "system/accessibility/select_to_speak_menu_bubble_controller.cc",
"system/accessibility/select_to_speak_menu_bubble_controller.h", "system/accessibility/select_to_speak_menu_bubble_controller.h",
"system/accessibility/select_to_speak_menu_view.cc", "system/accessibility/select_to_speak_menu_view.cc",
......
...@@ -807,7 +807,7 @@ This file contains the strings for ash. ...@@ -807,7 +807,7 @@ This file contains the strings for ash.
Exit Select-to-speak Exit Select-to-speak
</message> </message>
<message name="IDS_ASH_SELECT_TO_SPEAK_DEFAULT_OPTION" desc="The label for the Select-to-speak reading speed option where users can select their default system text-to-speech rate."> <message name="IDS_ASH_SELECT_TO_SPEAK_DEFAULT_OPTION" desc="The label for the Select-to-speak reading speed option where users can select their default system text-to-speech rate.">
Default (<ph name="RATE">$1<ex>1.2</ex></ph>x) Default
</message> </message>
<message name="IDS_ASH_SELECT_TO_SPEAK_MENU" desc="The name of the Select-to-speak floating menu."> <message name="IDS_ASH_SELECT_TO_SPEAK_MENU" desc="The name of the Select-to-speak floating menu.">
Select-to-speak menu Select-to-speak menu
......
acc5e3479941a13671c14d5040730fd8ad8b4d9d 367a104c882fcec22ab6c9ea42ded7a40543dc77
\ No newline at end of file \ No newline at end of file
...@@ -162,7 +162,11 @@ aggregate_vector_icons("ash_vector_icons") { ...@@ -162,7 +162,11 @@ aggregate_vector_icons("ash_vector_icons") {
"select_to_speak_play.icon", "select_to_speak_play.icon",
"select_to_speak_prev_paragraph.icon", "select_to_speak_prev_paragraph.icon",
"select_to_speak_prev_sentence.icon", "select_to_speak_prev_sentence.icon",
"select_to_speak_reading_speed.icon", "select_to_speak_reading_speed_fast.icon",
"select_to_speak_reading_speed_faster.icon",
"select_to_speak_reading_speed_normal.icon",
"select_to_speak_reading_speed_peppy.icon",
"select_to_speak_reading_speed_slow.icon",
"select_to_speak_stop.icon", "select_to_speak_stop.icon",
"send.icon", "send.icon",
"settings.icon", "settings.icon",
......
CANVAS_DIMENSIONS, 12, CANVAS_DIMENSIONS, 20,
MOVE_TO, 0.75f, 10.94f, MOVE_TO, 4, 15,
LINE_TO, 5.69f, 6, LINE_TO, 12, 10,
LINE_TO, 0.75f, 1.06f, LINE_TO, 4, 5,
LINE_TO, 1.81f, 0, V_LINE_TO, 15,
LINE_TO, 7.81f, 6,
LINE_TO, 1.81f, 12,
LINE_TO, 0.75f, 10.94f,
CLOSE, CLOSE,
MOVE_TO, 10.06f, 12, MOVE_TO, 16, 5,
H_LINE_TO, 11.56f, H_LINE_TO, 14,
LINE_TO, 11.56f, 0, V_LINE_TO, 15,
H_LINE_TO, 10.06f, H_LINE_TO, 16,
LINE_TO, 10.06f, 12, V_LINE_TO, 5,
CLOSE CLOSE
\ No newline at end of file
CANVAS_DIMENSIONS, 8, CANVAS_DIMENSIONS, 20,
MOVE_TO, 0.69f, 1.06f, MOVE_TO, 6.67f, 13.83f,
LINE_TO, 1.75f, 0, LINE_TO, 10.79f, 10,
LINE_TO, 7.75f, 6, LINE_TO, 6.67f, 6.18f,
LINE_TO, 1.75f, 12, LINE_TO, 7.94f, 5,
LINE_TO, 0.69f, 10.94f, LINE_TO, 13.33f, 10,
LINE_TO, 5.63f, 6, LINE_TO, 7.94f, 15,
CLOSE LINE_TO, 6.67f, 13.83f,
CLOSE
\ No newline at end of file
CANVAS_DIMENSIONS, 16, CANVAS_DIMENSIONS, 20,
MOVE_TO, 7, 2, MOVE_TO, 9, 5,
H_LINE_TO, 4, H_LINE_TO, 6,
V_LINE_TO, 14, V_LINE_TO, 15,
H_LINE_TO, 7,
V_LINE_TO, 2,
CLOSE,
MOVE_TO, 12, 2,
H_LINE_TO, 9, H_LINE_TO, 9,
V_LINE_TO, 14, V_LINE_TO, 5,
H_LINE_TO, 12, CLOSE,
V_LINE_TO, 2, MOVE_TO, 14, 5,
CLOSE H_LINE_TO, 11,
V_LINE_TO, 15,
H_LINE_TO, 14,
V_LINE_TO, 5,
CLOSE
\ No newline at end of file
...@@ -3,4 +3,4 @@ MOVE_TO, 6, 16, ...@@ -3,4 +3,4 @@ MOVE_TO, 6, 16,
LINE_TO, 16, 10, LINE_TO, 16, 10,
LINE_TO, 6, 4, LINE_TO, 6, 4,
V_LINE_TO, 16, V_LINE_TO, 16,
CLOSE CLOSE
\ No newline at end of file
CANVAS_DIMENSIONS, 12, CANVAS_DIMENSIONS, 20,
MOVE_TO, 11.25f, 1.06f, MOVE_TO, 16, 15,
LINE_TO, 6.32f, 6, LINE_TO, 8, 10,
LINE_TO, 11.25f, 10.94f, LINE_TO, 16, 5,
LINE_TO, 10.19f, 12, V_LINE_TO, 15,
LINE_TO, 4.19f, 6,
LINE_TO, 10.19f, 0,
LINE_TO, 11.25f, 1.06f,
CLOSE, CLOSE,
MOVE_TO, 1.94f, 0, MOVE_TO, 4, 5,
LINE_TO, 0.44f, 0, H_LINE_TO, 6,
LINE_TO, 0.44f, 12, V_LINE_TO, 15,
H_LINE_TO, 1.94f, H_LINE_TO, 4,
LINE_TO, 1.94f, 0, V_LINE_TO, 5,
CLOSE CLOSE
\ No newline at end of file
CANVAS_DIMENSIONS, 8, CANVAS_DIMENSIONS, 20,
MOVE_TO, 7.31f, 10.94f, MOVE_TO, 13.34f, 13.83f,
LINE_TO, 6.25f, 12, LINE_TO, 9.22f, 10,
LINE_TO, 0.25f, 6, LINE_TO, 13.34f, 6.18f,
LINE_TO, 6.25f, 0, LINE_TO, 12.07f, 5,
LINE_TO, 7.31f, 1.06f, LINE_TO, 6.67f, 10,
LINE_TO, 2.37f, 6, LINE_TO, 12.07f, 15,
CLOSE LINE_TO, 13.34f, 13.83f,
CLOSE
\ No newline at end of file
CANVAS_DIMENSIONS, 24,
MOVE_TO, 20.38f, 8.57f,
R_LINE_TO, -1.23f, 1.85f,
R_ARC_TO, 8, 8, 0, 0, 1, -0.22f, 7.58f,
H_LINE_TO, 5.07f,
ARC_TO, 8, 8, 0, 0, 1, 15.58f, 6.85f,
R_LINE_TO, 1.85f, -1.23f,
ARC_TO, 10, 10, 0, 0, 0, 3.35f, 19,
R_ARC_TO, 2, 2, 0, 0, 0, 1.72f, 1,
R_H_LINE_TO, 13.85f,
R_ARC_TO, 2, 2, 0, 0, 0, 1.74f, -1,
R_ARC_TO, 10, 10, 0, 0, 0, -0.27f, -10.44f,
CLOSE,
R_MOVE_TO, -9.79f, 6.84f,
R_ARC_TO, 2, 2, 0, 0, 0, 2.83f, 0,
R_LINE_TO, 5.66f, -8.49f,
R_LINE_TO, -8.49f, 5.66f,
R_ARC_TO, 2, 2, 0, 0, 0, 0, 2.83f,
CLOSE
CANVAS_DIMENSIONS, 20,
MOVE_TO, 16, 5,
V_LINE_TO, 7,
H_LINE_TO, 12,
V_LINE_TO, 9,
H_LINE_TO, 14,
CUBIC_TO, 15.1f, 9, 16, 9.89f, 16, 11,
V_LINE_TO, 13,
CUBIC_TO, 16, 14.11f, 15.1f, 15, 14, 15,
H_LINE_TO, 10,
V_LINE_TO, 13,
H_LINE_TO, 14,
V_LINE_TO, 11,
H_LINE_TO, 10,
V_LINE_TO, 5,
H_LINE_TO, 16,
CLOSE,
MOVE_TO, 6, 5,
V_LINE_TO, 15,
H_LINE_TO, 4,
V_LINE_TO, 7,
H_LINE_TO, 3,
V_LINE_TO, 5,
H_LINE_TO, 6,
CLOSE,
MOVE_TO, 9, 13,
V_LINE_TO, 15,
H_LINE_TO, 7,
V_LINE_TO, 13,
H_LINE_TO, 9,
CLOSE
\ No newline at end of file
CANVAS_DIMENSIONS, 20,
MOVE_TO, 12, 5,
LINE_TO, 14, 8.33f,
LINE_TO, 16, 5,
H_LINE_TO, 18,
LINE_TO, 15, 10,
LINE_TO, 18, 15,
H_LINE_TO, 16,
LINE_TO, 14, 11.67f,
LINE_TO, 12, 15,
H_LINE_TO, 10,
LINE_TO, 13, 10,
LINE_TO, 10, 5,
H_LINE_TO, 12,
CLOSE,
MOVE_TO, 7, 5,
CUBIC_TO, 8.1f, 5, 9, 5.89f, 9, 7,
V_LINE_TO, 9,
CUBIC_TO, 9, 10.11f, 8.1f, 11, 7, 11,
H_LINE_TO, 5,
V_LINE_TO, 13,
H_LINE_TO, 9,
V_LINE_TO, 15,
H_LINE_TO, 3,
V_LINE_TO, 11,
CUBIC_TO, 3, 9.89f, 3.9f, 9, 5, 9,
H_LINE_TO, 7,
V_LINE_TO, 7,
H_LINE_TO, 3,
V_LINE_TO, 5,
H_LINE_TO, 7,
CLOSE
\ No newline at end of file
CANVAS_DIMENSIONS, 20,
MOVE_TO, 7, 5,
R_V_LINE_TO, 10,
H_LINE_TO, 5,
V_LINE_TO, 7,
H_LINE_TO, 4,
V_LINE_TO, 5,
R_H_LINE_TO, 3,
CLOSE,
R_MOVE_TO, 4, 0,
R_LINE_TO, 2, 3.33f,
LINE_TO, 15, 5,
R_H_LINE_TO, 2,
R_LINE_TO, -3, 5,
R_LINE_TO, 3, 5,
R_H_LINE_TO, -2,
R_LINE_TO, -2, -3.33f,
LINE_TO, 11, 15,
H_LINE_TO, 9,
R_LINE_TO, 3, -5,
R_LINE_TO, -3, -5,
R_H_LINE_TO, 2,
CLOSE
\ No newline at end of file
CANVAS_DIMENSIONS, 20,
MOVE_TO, 6, 5,
V_LINE_TO, 15,
H_LINE_TO, 4,
V_LINE_TO, 7,
H_LINE_TO, 3,
V_LINE_TO, 5,
H_LINE_TO, 6,
CLOSE,
MOVE_TO, 9, 13,
V_LINE_TO, 15,
H_LINE_TO, 7,
V_LINE_TO, 13,
H_LINE_TO, 9,
CLOSE,
MOVE_TO, 14, 5,
CUBIC_TO, 15.1f, 5, 16, 5.89f, 16, 7,
V_LINE_TO, 9,
CUBIC_TO, 16, 10.11f, 15.1f, 11, 14, 11,
H_LINE_TO, 12,
V_LINE_TO, 13,
H_LINE_TO, 16,
V_LINE_TO, 15,
H_LINE_TO, 10,
V_LINE_TO, 11,
CUBIC_TO, 10, 9.89f, 10.9f, 9, 12, 9,
H_LINE_TO, 14,
V_LINE_TO, 7,
H_LINE_TO, 10,
V_LINE_TO, 5,
H_LINE_TO, 14,
CLOSE
\ No newline at end of file
CANVAS_DIMENSIONS, 20,
MOVE_TO, 18, 5,
V_LINE_TO, 7,
H_LINE_TO, 14,
V_LINE_TO, 9,
H_LINE_TO, 16,
CUBIC_TO, 17.1f, 9, 18, 9.89f, 18, 11,
V_LINE_TO, 13,
CUBIC_TO, 18, 14.11f, 17.1f, 15, 16, 15,
H_LINE_TO, 12,
V_LINE_TO, 13,
H_LINE_TO, 16,
V_LINE_TO, 11,
H_LINE_TO, 12,
V_LINE_TO, 5,
H_LINE_TO, 18,
CLOSE,
MOVE_TO, 6, 5,
CUBIC_TO, 7.1f, 5, 8, 5.9f, 8, 7,
V_LINE_TO, 13,
CUBIC_TO, 8, 14.1f, 7.1f, 15, 6, 15,
H_LINE_TO, 4,
CUBIC_TO, 2.9f, 15, 2, 14.1f, 2, 13,
V_LINE_TO, 7,
CUBIC_TO, 2, 5.9f, 2.9f, 5, 4, 5,
H_LINE_TO, 6,
CLOSE,
MOVE_TO, 11, 13,
V_LINE_TO, 15,
H_LINE_TO, 9,
V_LINE_TO, 13,
H_LINE_TO, 11,
CLOSE,
MOVE_TO, 6, 7,
H_LINE_TO, 4,
V_LINE_TO, 13,
H_LINE_TO, 6,
V_LINE_TO, 7,
CLOSE
\ No newline at end of file
CANVAS_DIMENSIONS, 9, CANVAS_DIMENSIONS, 20,
ROUND_RECT, 0, 0, 9, 9, 0 MOVE_TO, 10, 2,
\ No newline at end of file CUBIC_TO, 5.58f, 2, 2, 5.58f, 2, 10,
CUBIC_TO, 2, 14.42f, 5.58f, 18, 10, 18,
CUBIC_TO, 14.42f, 18, 18, 14.42f, 18, 10,
CUBIC_TO, 18, 5.58f, 14.42f, 2, 10, 2,
CLOSE,
MOVE_TO, 4, 10,
CUBIC_TO, 4, 13.31f, 6.69f, 16, 10, 16,
CUBIC_TO, 13.31f, 16, 16, 13.31f, 16, 10,
CUBIC_TO, 16, 6.69f, 13.31f, 4, 10, 4,
CUBIC_TO, 6.69f, 4, 4, 6.69f, 4, 10,
CLOSE,
MOVE_TO, 7, 7,
H_LINE_TO, 13,
V_LINE_TO, 13,
H_LINE_TO, 7,
V_LINE_TO, 7,
CLOSE
\ No newline at end of file
// Copyright 2020 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_SYSTEM_ACCESSIBILITY_SELECT_TO_SPEAK_CONSTANTS_H_
#define ASH_SYSTEM_ACCESSIBILITY_SELECT_TO_SPEAK_CONSTANTS_H_
namespace ash {
// User-selectable speech rates.
constexpr double kSelectToSpeakSpeechRateSlow = 0.5;
constexpr double kSelectToSpeakSpeechRateNormal = 1.0;
constexpr double kSelectToSpeakSpeechRatePeppy = 1.2;
constexpr double kSelectToSpeakSpeechRateFast = 1.5;
constexpr double kSelectToSpeakSpeechRateFaster = 2.0;
const double kSelectToSpeakSpeechRates[] = {
kSelectToSpeakSpeechRateSlow, kSelectToSpeakSpeechRateNormal,
kSelectToSpeakSpeechRatePeppy, kSelectToSpeakSpeechRateFast,
kSelectToSpeakSpeechRateFaster,
};
} // namespace ash
#endif // ASH_SYSTEM_ACCESSIBILITY_SELECT_TO_SPEAK_CONSTANTS_H_
\ No newline at end of file
...@@ -59,6 +59,7 @@ void SelectToSpeakMenuBubbleController::Show(const gfx::Rect& anchor, ...@@ -59,6 +59,7 @@ void SelectToSpeakMenuBubbleController::Show(const gfx::Rect& anchor,
menu_view_->SetBorder( menu_view_->SetBorder(
views::CreateEmptyBorder(kUnifiedTopShortcutSpacing, 0, 0, 0)); views::CreateEmptyBorder(kUnifiedTopShortcutSpacing, 0, 0, 0));
bubble_view_->AddChildView(menu_view_); bubble_view_->AddChildView(menu_view_);
menu_view_->SetSpeedButtonToggled(false);
menu_view_->SetPaintToLayer(); menu_view_->SetPaintToLayer();
menu_view_->layer()->SetFillsBoundsOpaquely(false); menu_view_->layer()->SetFillsBoundsOpaquely(false);
...@@ -70,6 +71,7 @@ void SelectToSpeakMenuBubbleController::Show(const gfx::Rect& anchor, ...@@ -70,6 +71,7 @@ void SelectToSpeakMenuBubbleController::Show(const gfx::Rect& anchor,
// Update button states. // Update button states.
menu_view_->SetPaused(is_paused); menu_view_->SetPaused(is_paused);
menu_view_->SetInitialSpeechRate(initial_speech_rate);
// Add vertical spacing to given anchor rect. // Add vertical spacing to given anchor rect.
bubble_view_->ChangeAnchorRect(gfx::Rect( bubble_view_->ChangeAnchorRect(gfx::Rect(
...@@ -86,7 +88,6 @@ void SelectToSpeakMenuBubbleController::Hide() { ...@@ -86,7 +88,6 @@ void SelectToSpeakMenuBubbleController::Hide() {
bubble_widget_->Hide(); bubble_widget_->Hide();
} }
if (speed_bubble_controller_) { if (speed_bubble_controller_) {
speed_bubble_controller_->Hide();
speed_bubble_controller_.reset(); speed_bubble_controller_.reset();
} }
} }
...@@ -121,18 +122,31 @@ void SelectToSpeakMenuBubbleController::OnActionSelected( ...@@ -121,18 +122,31 @@ void SelectToSpeakMenuBubbleController::OnActionSelected(
// Toggle reading speed selection menu. // Toggle reading speed selection menu.
if (!speed_bubble_controller_) { if (!speed_bubble_controller_) {
speed_bubble_controller_ = speed_bubble_controller_ =
std::make_unique<SelectToSpeakSpeedBubbleController>(); std::make_unique<SelectToSpeakSpeedBubbleController>(this);
} }
if (speed_bubble_controller_->IsVisible()) { if (speed_bubble_controller_->IsVisible()) {
speed_bubble_controller_->Hide();
speed_bubble_controller_.reset(); speed_bubble_controller_.reset();
menu_view_->SetSpeedButtonToggled(false);
} else { } else {
speed_bubble_controller_->Show( speed_bubble_controller_->Show(
/*anchor=*/menu_view_, initial_speech_rate_); /*anchor=*/menu_view_, initial_speech_rate_);
menu_view_->SetSpeedButtonToggled(true);
} }
return;
} }
Shell::Get()->accessibility_controller()->OnSelectToSpeakPanelAction( Shell::Get()->accessibility_controller()->OnSelectToSpeakPanelAction(
action, /*value=*/0.0); action, /*value=*/0.0);
} }
void SelectToSpeakMenuBubbleController::OnSpeechRateSelected(
double speech_rate) {
if (speed_bubble_controller_) {
speed_bubble_controller_->Hide();
speed_bubble_controller_.reset();
menu_view_->SetSpeedButtonToggled(false);
}
Shell::Get()->accessibility_controller()->OnSelectToSpeakPanelAction(
SelectToSpeakPanelAction::kChangeSpeed, /*value=*/speech_rate);
}
} // namespace ash } // namespace ash
\ No newline at end of file
...@@ -18,6 +18,7 @@ namespace ash { ...@@ -18,6 +18,7 @@ namespace ash {
class ASH_EXPORT SelectToSpeakMenuBubbleController class ASH_EXPORT SelectToSpeakMenuBubbleController
: public TrayBubbleView::Delegate, : public TrayBubbleView::Delegate,
public SelectToSpeakMenuView::Delegate, public SelectToSpeakMenuView::Delegate,
public SelectToSpeakSpeedView::Delegate,
public ::wm::ActivationChangeObserver { public ::wm::ActivationChangeObserver {
public: public:
SelectToSpeakMenuBubbleController(); SelectToSpeakMenuBubbleController();
...@@ -47,6 +48,9 @@ class ASH_EXPORT SelectToSpeakMenuBubbleController ...@@ -47,6 +48,9 @@ class ASH_EXPORT SelectToSpeakMenuBubbleController
// SelectToSpeakMenuView::Delegate: // SelectToSpeakMenuView::Delegate:
void OnActionSelected(SelectToSpeakPanelAction action) override; void OnActionSelected(SelectToSpeakPanelAction action) override;
// SelectToSpeakSpeedView::Delegate:
void OnSpeechRateSelected(double speech_rate) override;
// Owned by views hierarchy. // Owned by views hierarchy.
TrayBubbleView* bubble_view_ = nullptr; TrayBubbleView* bubble_view_ = nullptr;
views::Widget* bubble_widget_ = nullptr; views::Widget* bubble_widget_ = nullptr;
......
...@@ -246,9 +246,8 @@ TEST_F(SelectToSpeakMenuBubbleControllerTest, ChangeSpeedButtonPressed) { ...@@ -246,9 +246,8 @@ TEST_F(SelectToSpeakMenuBubbleControllerTest, ChangeSpeedButtonPressed) {
EXPECT_TRUE(GetSpeedBubbleController() && EXPECT_TRUE(GetSpeedBubbleController() &&
GetSpeedBubbleController()->IsVisible()); GetSpeedBubbleController()->IsVisible());
// Clicking panel hides the speed selection bubble. // Clicking button again closes the speed selection bubble.
GetEventGenerator()->GestureTapAt( GetEventGenerator()->GestureTapAt(button->GetBoundsInScreen().CenterPoint());
GetMenuView()->GetBoundsInScreen().CenterPoint());
EXPECT_TRUE(!GetSpeedBubbleController() || EXPECT_TRUE(!GetSpeedBubbleController() ||
!GetSpeedBubbleController()->IsVisible()); !GetSpeedBubbleController()->IsVisible());
} }
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "ash/strings/grit/ash_strings.h" #include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h" #include "ash/style/ash_color_provider.h"
#include "ash/system/accessibility/floating_menu_button.h" #include "ash/system/accessibility/floating_menu_button.h"
#include "ash/system/accessibility/select_to_speak_constants.h"
#include "ash/system/tray/tray_constants.h" #include "ash/system/tray/tray_constants.h"
#include "base/bind.h" #include "base/bind.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
...@@ -123,7 +124,7 @@ SelectToSpeakMenuView::SelectToSpeakMenuView(Delegate* delegate) ...@@ -123,7 +124,7 @@ SelectToSpeakMenuView::SelectToSpeakMenuView(Delegate* delegate)
views::Builder<FloatingMenuButton>() views::Builder<FloatingMenuButton>()
.CopyAddressTo(&speed_button_) .CopyAddressTo(&speed_button_)
.SetID(static_cast<int>(ButtonId::kSpeed)) .SetID(static_cast<int>(ButtonId::kSpeed))
.SetVectorIcon(kSelectToSpeakReadingSpeedIcon) .SetVectorIcon(kSelectToSpeakReadingSpeedNormalIcon)
.SetTooltipText(l10n_util::GetStringUTF16( .SetTooltipText(l10n_util::GetStringUTF16(
IDS_ASH_SELECT_TO_SPEAK_READING_SPEED)) IDS_ASH_SELECT_TO_SPEAK_READING_SPEED))
.SetCallback(base::BindRepeating( .SetCallback(base::BindRepeating(
...@@ -155,8 +156,20 @@ SelectToSpeakMenuView::SelectToSpeakMenuView(Delegate* delegate) ...@@ -155,8 +156,20 @@ SelectToSpeakMenuView::SelectToSpeakMenuView(Delegate* delegate)
base::Unretained(this), base::Unretained(this),
base::Unretained(stop_button_)))})}) base::Unretained(stop_button_)))})})
.BuildChildren(); .BuildChildren();
}
pause_button_->SetToggled(true); void SelectToSpeakMenuView::SetInitialSpeechRate(double initial_speech_rate) {
const gfx::VectorIcon* speed_icon = &kSelectToSpeakReadingSpeedNormalIcon;
if (initial_speech_rate == kSelectToSpeakSpeechRateSlow) {
speed_icon = &kSelectToSpeakReadingSpeedSlowIcon;
} else if (initial_speech_rate == kSelectToSpeakSpeechRatePeppy) {
speed_icon = &kSelectToSpeakReadingSpeedPeppyIcon;
} else if (initial_speech_rate == kSelectToSpeakSpeechRateFast) {
speed_icon = &kSelectToSpeakReadingSpeedFastIcon;
} else if (initial_speech_rate == kSelectToSpeakSpeechRateFaster) {
speed_icon = &kSelectToSpeakReadingSpeedFasterIcon;
}
speed_button_->SetVectorIcon(*speed_icon);
} }
void SelectToSpeakMenuView::OnKeyEvent(ui::KeyEvent* key_event) { void SelectToSpeakMenuView::OnKeyEvent(ui::KeyEvent* key_event) {
...@@ -204,6 +217,10 @@ void SelectToSpeakMenuView::SetInitialFocus() { ...@@ -204,6 +217,10 @@ void SelectToSpeakMenuView::SetInitialFocus() {
pause_button_->RequestFocus(); pause_button_->RequestFocus();
} }
void SelectToSpeakMenuView::SetSpeedButtonToggled(bool toggled) {
speed_button_->SetToggled(toggled);
}
void SelectToSpeakMenuView::OnButtonPressed(views::Button* sender) { void SelectToSpeakMenuView::OnButtonPressed(views::Button* sender) {
SelectToSpeakPanelAction action = SelectToSpeakPanelAction action =
PanelActionForButtonID(sender->GetID(), is_paused_); PanelActionForButtonID(sender->GetID(), is_paused_);
......
...@@ -54,6 +54,12 @@ class SelectToSpeakMenuView : public views::BoxLayoutView { ...@@ -54,6 +54,12 @@ class SelectToSpeakMenuView : public views::BoxLayoutView {
void SetInitialFocus(); void SetInitialFocus();
// Sets the speech rate that should be selected.
void SetInitialSpeechRate(double initial_speech_rate);
// Sets reading speed button into toggled state.
void SetSpeedButtonToggled(bool toggled);
private: private:
void OnButtonPressed(views::Button* sender); void OnButtonPressed(views::Button* sender);
......
...@@ -24,7 +24,9 @@ constexpr int kBubbleViewMargin = 2; ...@@ -24,7 +24,9 @@ constexpr int kBubbleViewMargin = 2;
} // namespace } // namespace
SelectToSpeakSpeedBubbleController::SelectToSpeakSpeedBubbleController() { SelectToSpeakSpeedBubbleController::SelectToSpeakSpeedBubbleController(
SelectToSpeakSpeedView::Delegate* delegate)
: delegate_(delegate) {
Shell::Get()->activation_client()->AddObserver(this); Shell::Get()->activation_client()->AddObserver(this);
} }
...@@ -67,6 +69,8 @@ void SelectToSpeakSpeedBubbleController::Show(views::View* anchor_view, ...@@ -67,6 +69,8 @@ void SelectToSpeakSpeedBubbleController::Show(views::View* anchor_view,
views::BubbleDialogDelegateView::CreateBubble(bubble_view_); views::BubbleDialogDelegateView::CreateBubble(bubble_view_);
TrayBackgroundView::InitializeBubbleAnimations(bubble_widget_); TrayBackgroundView::InitializeBubbleAnimations(bubble_widget_);
bubble_view_->InitializeAndShowBubble(); bubble_view_->InitializeAndShowBubble();
} else {
speed_view_->SetInitialSpeechRate(speech_rate);
} }
bubble_view_->ChangeAnchorView(anchor_view); bubble_view_->ChangeAnchorView(anchor_view);
...@@ -99,16 +103,14 @@ void SelectToSpeakSpeedBubbleController::OnWindowActivated( ...@@ -99,16 +103,14 @@ void SelectToSpeakSpeedBubbleController::OnWindowActivated(
views::Widget::GetWidgetForNativeView(gained_active); views::Widget::GetWidgetForNativeView(gained_active);
if (gained_widget == bubble_widget_) { if (gained_widget == bubble_widget_) {
speed_view_->SetInitialFocus(); speed_view_->SetInitialFocus();
} else {
Hide();
} }
} }
void SelectToSpeakSpeedBubbleController::OnSpeechRateSelected( void SelectToSpeakSpeedBubbleController::OnSpeechRateSelected(
double speech_rate) { double speech_rate) {
Shell::Get()->accessibility_controller()->OnSelectToSpeakPanelAction( // Let parent handle this, so menu bubble controller can properly set speed
SelectToSpeakPanelAction::kChangeSpeed, speech_rate); // button state.
Hide(); delegate_->OnSpeechRateSelected(speech_rate);
} }
} // namespace ash } // namespace ash
\ No newline at end of file
...@@ -19,7 +19,8 @@ class ASH_EXPORT SelectToSpeakSpeedBubbleController ...@@ -19,7 +19,8 @@ class ASH_EXPORT SelectToSpeakSpeedBubbleController
public SelectToSpeakSpeedView::Delegate, public SelectToSpeakSpeedView::Delegate,
public ::wm::ActivationChangeObserver { public ::wm::ActivationChangeObserver {
public: public:
SelectToSpeakSpeedBubbleController(); explicit SelectToSpeakSpeedBubbleController(
SelectToSpeakSpeedView::Delegate* delegate);
SelectToSpeakSpeedBubbleController( SelectToSpeakSpeedBubbleController(
const SelectToSpeakSpeedBubbleController&) = delete; const SelectToSpeakSpeedBubbleController&) = delete;
SelectToSpeakSpeedBubbleController& operator=( SelectToSpeakSpeedBubbleController& operator=(
...@@ -53,6 +54,9 @@ class ASH_EXPORT SelectToSpeakSpeedBubbleController ...@@ -53,6 +54,9 @@ class ASH_EXPORT SelectToSpeakSpeedBubbleController
TrayBubbleView* bubble_view_ = nullptr; TrayBubbleView* bubble_view_ = nullptr;
views::Widget* bubble_widget_ = nullptr; views::Widget* bubble_widget_ = nullptr;
SelectToSpeakSpeedView* speed_view_ = nullptr; SelectToSpeakSpeedView* speed_view_ = nullptr;
// Owned by parent whose lifetime exceeds this class.
SelectToSpeakSpeedView::Delegate* delegate_ = nullptr;
}; };
} // namespace ash } // namespace ash
......
...@@ -147,7 +147,7 @@ TEST_F(SelectToSpeakSpeedBubbleControllerTest, SelectFastOption) { ...@@ -147,7 +147,7 @@ TEST_F(SelectToSpeakSpeedBubbleControllerTest, SelectFastOption) {
EXPECT_EQ(client.last_select_to_speak_panel_action(), EXPECT_EQ(client.last_select_to_speak_panel_action(),
SelectToSpeakPanelAction::kChangeSpeed); SelectToSpeakPanelAction::kChangeSpeed);
EXPECT_EQ(client.last_select_to_speak_panel_action_value(), 1.6); EXPECT_EQ(client.last_select_to_speak_panel_action_value(), 1.5);
} }
TEST_F(SelectToSpeakSpeedBubbleControllerTest, SelectFasterOption) { TEST_F(SelectToSpeakSpeedBubbleControllerTest, SelectFasterOption) {
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "ash/strings/grit/ash_strings.h" #include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h" #include "ash/style/ash_color_provider.h"
#include "ash/system/accessibility/select_to_speak_constants.h"
#include "ash/system/tray/hover_highlight_view.h" #include "ash/system/tray/hover_highlight_view.h"
#include "ash/system/tray/tray_popup_utils.h" #include "ash/system/tray/tray_popup_utils.h"
#include "base/bind.h" #include "base/bind.h"
...@@ -22,9 +23,6 @@ namespace ash { ...@@ -22,9 +23,6 @@ namespace ash {
namespace { namespace {
// User-selectable speech rates.
const std::vector<double> kSpeechRates = {0.5, 1.0, 1.2, 1.6, 2.0};
// View ID for the option that represents the system default speech rate. // View ID for the option that represents the system default speech rate.
constexpr int kDefaultSpeedId = 100; constexpr int kDefaultSpeedId = 100;
...@@ -37,10 +35,15 @@ SelectToSpeakSpeedView::SelectToSpeakSpeedView(Delegate* delegate, ...@@ -37,10 +35,15 @@ SelectToSpeakSpeedView::SelectToSpeakSpeedView(Delegate* delegate,
double initial_speech_rate) double initial_speech_rate)
: delegate_(delegate) { : delegate_(delegate) {
SetOrientation(views::BoxLayout::Orientation::kVertical); SetOrientation(views::BoxLayout::Orientation::kVertical);
SetInitialSpeechRate(initial_speech_rate);
}
void SelectToSpeakSpeedView::SetInitialSpeechRate(double initial_speech_rate) {
RemoveAllChildViews(true);
bool any_selected = false; bool any_selected = false;
for (unsigned int i = 0; i < kSpeechRates.size(); i++) { for (size_t i = 0; i < base::size(kSelectToSpeakSpeechRates); i++) {
double option_speed = kSpeechRates[i]; double option_speed = kSelectToSpeakSpeechRates[i];
bool is_selected = option_speed == initial_speech_rate; bool is_selected = option_speed == initial_speech_rate;
// Add 1 to the index, because view IDs cannot be 0. // Add 1 to the index, because view IDs cannot be 0.
auto label = base::ASCIIToUTF16(base::StringPrintf("%.1fx", option_speed)); auto label = base::ASCIIToUTF16(base::StringPrintf("%.1fx", option_speed));
...@@ -49,9 +52,8 @@ SelectToSpeakSpeedView::SelectToSpeakSpeedView(Delegate* delegate, ...@@ -49,9 +52,8 @@ SelectToSpeakSpeedView::SelectToSpeakSpeedView(Delegate* delegate,
} }
if (!any_selected) { if (!any_selected) {
default_speech_rate_ = initial_speech_rate; default_speech_rate_ = initial_speech_rate;
auto label = l10n_util::GetStringFUTF16( auto label =
IDS_ASH_SELECT_TO_SPEAK_DEFAULT_OPTION, l10n_util::GetStringUTF16(IDS_ASH_SELECT_TO_SPEAK_DEFAULT_OPTION);
base::ASCIIToUTF16(base::StringPrintf("%.1f", initial_speech_rate)));
AddMenuItem(/*option_id=*/kDefaultSpeedId, label, /*is_selected=*/true); AddMenuItem(/*option_id=*/kDefaultSpeedId, label, /*is_selected=*/true);
} }
} }
...@@ -69,8 +71,8 @@ void SelectToSpeakSpeedView::AddMenuItem(int option_id, ...@@ -69,8 +71,8 @@ void SelectToSpeakSpeedView::AddMenuItem(int option_id,
void SelectToSpeakSpeedView::OnViewClicked(views::View* sender) { void SelectToSpeakSpeedView::OnViewClicked(views::View* sender) {
unsigned int speed_index = sender->GetID() - 1; unsigned int speed_index = sender->GetID() - 1;
double selected_rate = default_speech_rate_; double selected_rate = default_speech_rate_;
if (speed_index >= 0 && speed_index < kSpeechRates.size()) if (speed_index >= 0 && speed_index < base::size(kSelectToSpeakSpeechRates))
selected_rate = kSpeechRates[speed_index]; selected_rate = kSelectToSpeakSpeechRates[speed_index];
if (selected_rate != 0.0) if (selected_rate != 0.0)
delegate_->OnSpeechRateSelected(selected_rate); delegate_->OnSpeechRateSelected(selected_rate);
} }
......
...@@ -38,6 +38,9 @@ class SelectToSpeakSpeedView : public views::BoxLayoutView, ...@@ -38,6 +38,9 @@ class SelectToSpeakSpeedView : public views::BoxLayoutView,
void SetInitialFocus(); void SetInitialFocus();
// Sets the speech rate that should be selected.
void SetInitialSpeechRate(double initial_speech_rate);
private: private:
void AddMenuItem(int option_id, void AddMenuItem(int option_id,
const base::string16& label, const base::string16& label,
......
...@@ -94,5 +94,8 @@ MockTts.prototype = { ...@@ -94,5 +94,8 @@ MockTts.prototype = {
}, },
setOnSpeechCallbacks(callbacks) { setOnSpeechCallbacks(callbacks) {
this.speechCallbackStack_ = callbacks.reverse(); this.speechCallbackStack_ = callbacks.reverse();
},
getOptions() {
return this.options_;
} }
}; };
...@@ -41,6 +41,9 @@ const GSUITE_APP_REGEXP = ...@@ -41,6 +41,9 @@ const GSUITE_APP_REGEXP =
// AshColorProvider::ShieldLayerType kShield40. // AshColorProvider::ShieldLayerType kShield40.
const DEFAULT_BACKGROUND_SHADING_COLOR = '#0006'; const DEFAULT_BACKGROUND_SHADING_COLOR = '#0006';
// Settings key for system speech rate setting.
const SPEECH_RATE_KEY = 'settings.tts.speech_rate';
/** /**
* Determines if a node is in one of the known Google GSuite apps that needs * Determines if a node is in one of the known Google GSuite apps that needs
* special case treatment for speaking selected text. Not all Google GSuite * special case treatment for speaking selected text. Not all Google GSuite
...@@ -251,6 +254,18 @@ class SelectToSpeak { ...@@ -251,6 +254,18 @@ class SelectToSpeak {
AccessibilityFeature.SELECT_TO_SPEAK_NAVIGATION_CONTROL, (result) => { AccessibilityFeature.SELECT_TO_SPEAK_NAVIGATION_CONTROL, (result) => {
this.navigationControlFlag_ = result; this.navigationControlFlag_ = result;
}); });
/** @private {number} Default speech rate set in system settings. */
this.systemSpeechRate_ = 1.0;
chrome.settingsPrivate.getPref(SPEECH_RATE_KEY, (pref) => {
if (!pref) {
return;
}
this.systemSpeechRate_ = /** @type {number} */ (pref.value);
});
/** @private {?number} Speech rate that overrides system rate. */
this.overrideSpeechRate_ = null;
} }
/** /**
...@@ -669,6 +684,7 @@ class SelectToSpeak { ...@@ -669,6 +684,7 @@ class SelectToSpeak {
chrome.tts.stop(); chrome.tts.stop();
this.clearFocusRing_(); this.clearFocusRing_();
this.clearNavigationStateVariables_(); this.clearNavigationStateVariables_();
this.overrideSpeechRate_ = null; // Reset speech rate to system default
this.onStateChanged_(SelectToSpeakState.INACTIVE); this.onStateChanged_(SelectToSpeakState.INACTIVE);
} }
...@@ -713,7 +729,8 @@ class SelectToSpeak { ...@@ -713,7 +729,8 @@ class SelectToSpeak {
// Also, update the location of the panel according to the focus ring. // Also, update the location of the panel according to the focus ring.
chrome.accessibilityPrivate.updateSelectToSpeakPanel( chrome.accessibilityPrivate.updateSelectToSpeakPanel(
/* show= */ true, /* anchor= */ this.currentFocusRing_[0], /* show= */ true, /* anchor= */ this.currentFocusRing_[0],
/* isPaused= */ this.isPaused_(), /* speed= */ 1.2); /* isPaused= */ this.isPaused_(),
/* speed= */ this.getSpeechRate_());
} else { } else {
// Dismiss the panel if either the feature is disabled or the focus ring // Dismiss the panel if either the feature is disabled or the focus ring
// is not valid. // is not valid.
...@@ -827,6 +844,8 @@ class SelectToSpeak { ...@@ -827,6 +844,8 @@ class SelectToSpeak {
this.onStateChangeRequested_.bind(this)); this.onStateChangeRequested_.bind(this));
chrome.accessibilityPrivate.onSelectToSpeakPanelAction.addListener( chrome.accessibilityPrivate.onSelectToSpeakPanelAction.addListener(
this.onSelectToSpeakPanelAction_.bind(this)); this.onSelectToSpeakPanelAction_.bind(this));
chrome.settingsPrivate.onPrefsChanged.addListener(
this.onPrefsChanged_.bind(this));
// Initialize the state to SelectToSpeakState.INACTIVE. // Initialize the state to SelectToSpeakState.INACTIVE.
chrome.accessibilityPrivate.setSelectToSpeakState(this.state_); chrome.accessibilityPrivate.setSelectToSpeakState(this.state_);
} }
...@@ -866,10 +885,11 @@ class SelectToSpeak { ...@@ -866,10 +885,11 @@ class SelectToSpeak {
/** /**
* Handles Select-to-speak panel action. * Handles Select-to-speak panel action.
* @param {!SelectToSpeakPanelAction} panelAction * @param {!SelectToSpeakPanelAction} panelAction Action to perform.
* @param {number=} value Optional value associated with action.
* @private * @private
*/ */
onSelectToSpeakPanelAction_(panelAction) { onSelectToSpeakPanelAction_(panelAction, value) {
if (!this.shouldShowNavigationControls_()) { if (!this.shouldShowNavigationControls_()) {
// Ignore if this feature is not enabled. // Ignore if this feature is not enabled.
return; return;
...@@ -896,11 +916,31 @@ class SelectToSpeak { ...@@ -896,11 +916,31 @@ class SelectToSpeak {
case SelectToSpeakPanelAction.RESUME: case SelectToSpeakPanelAction.RESUME:
this.resume_(); this.resume_();
break; break;
case SelectToSpeakPanelAction.CHANGE_SPEED:
if (!value) {
console.warn(
'Change speed request receieved with invalid value', value);
return;
}
this.changeSpeed_(value);
break;
default: default:
// TODO(crbug.com/1140216): Implement other actions. // TODO(crbug.com/1140216): Implement other actions.
} }
} }
/**
* Handles system preferences change.
* @param {!Array<!Object>} prefs
* @private
*/
onPrefsChanged_(prefs) {
const ratePref = prefs.find((pref) => pref.key === SPEECH_RATE_KEY);
if (ratePref) {
this.systemSpeechRate_ = ratePref.value;
}
}
/** /**
* Navigate to the next sentence. * Navigate to the next sentence.
* @param {constants.Dir} direction whether to find the next sentence or * @param {constants.Dir} direction whether to find the next sentence or
...@@ -972,6 +1012,21 @@ class SelectToSpeak { ...@@ -972,6 +1012,21 @@ class SelectToSpeak {
this.startSpeechQueue_(nextParagraphNodes); this.startSpeechQueue_(nextParagraphNodes);
} }
/**
* Updates current reading speed (speech rate).
* @param {number} rate
* @private
*/
async changeSpeed_(rate) {
this.overrideSpeechRate_ = rate === this.systemSpeechRate_ ? null : rate;
// If currently playing, stop TTS, then resume from current spot.
if (!this.isPaused_()) {
await this.pause_();
this.resume_();
}
}
/** /**
* Enqueue speech for the single given string. The string is not associated * Enqueue speech for the single given string. The string is not associated
* with any particular nodes, so this does not do any work around drawing * with any particular nodes, so this does not do any work around drawing
...@@ -1137,6 +1192,9 @@ class SelectToSpeak { ...@@ -1137,6 +1192,9 @@ class SelectToSpeak {
nodeGroup.detectedLanguage) { nodeGroup.detectedLanguage) {
options.lang = nodeGroup.detectedLanguage; options.lang = nodeGroup.detectedLanguage;
} }
if (this.navigationControlFlag_) {
options.rate = this.getSpeechRate_();
}
const nodeGroupText = nodeGroup.text || ''; const nodeGroupText = nodeGroup.text || '';
...@@ -1647,6 +1705,14 @@ class SelectToSpeak { ...@@ -1647,6 +1705,14 @@ class SelectToSpeak {
} }
} }
/**
* @return {number} Current speech rate.
* @private
*/
getSpeechRate_() {
return this.overrideSpeechRate_ || this.systemSpeechRate_;
}
/** /**
* Fires a mock key down event for testing. * Fires a mock key down event for testing.
* @param {!Event} event The fake key down event to fire. The object * @param {!Event} event The fake key down event to fire. The object
......
...@@ -302,4 +302,85 @@ TEST_F('SelectToSpeakNavigationControlTest', 'PrevSentence', function() { ...@@ -302,4 +302,85 @@ TEST_F('SelectToSpeakNavigationControlTest', 'PrevSentence', function() {
this.mockTts.pendingUtterances()[0], this.mockTts.pendingUtterances()[0],
'Second sentence. Third sentence.'); 'Second sentence. Third sentence.');
}); });
}); });
\ No newline at end of file
TEST_F(
'SelectToSpeakNavigationControlTest', 'ChangeSpeedWhilePlaying',
function() {
chrome.settingsPrivate.setPref('settings.tts.speech_rate', 1.2);
const bodyHtml = `
<p id="p1">Paragraph 1</p>'
`;
this.runWithLoadedTree(
this.generateHtmlWithSelectedElement('p1', bodyHtml), () => {
this.triggerReadSelectedText();
// Speaks the first word.
this.mockTts.speakUntilCharIndex(10);
assertTrue(this.mockTts.currentlySpeaking());
assertEquals(this.mockTts.pendingUtterances().length, 1);
this.assertEqualsCollapseWhitespace(
this.mockTts.pendingUtterances()[0], 'Paragraph 1');
assertEquals(this.mockTts.getOptions().rate, 1.2);
// Changing speed will resume where we left off with selected speech
// rate.
selectToSpeak.onSelectToSpeakPanelAction_(
chrome.accessibilityPrivate.SelectToSpeakPanelAction
.CHANGE_SPEED,
1.5);
assertFalse(this.mockTts.currentlySpeaking());
assertEquals(this.mockTts.pendingUtterances().length, 0);
// Wait an event loop so all pending promises are resolved prior to
// asserting that TTS resumed with the proper rate.
setTimeout(
this.newCallback(() => {
assertTrue(this.mockTts.currentlySpeaking());
assertEquals(this.mockTts.getOptions().rate, 1.5);
assertEquals(this.mockTts.pendingUtterances().length, 1);
this.assertEqualsCollapseWhitespace(
this.mockTts.pendingUtterances()[0], '1');
}),
0);
});
});
TEST_F(
'SelectToSpeakNavigationControlTest', 'ChangeSpeedWhilePaused', function() {
chrome.settingsPrivate.setPref('settings.tts.speech_rate', 1.2);
const bodyHtml = `
<p id="p1">Paragraph 1</p>'
`;
this.runWithLoadedTree(
this.generateHtmlWithSelectedElement('p1', bodyHtml), () => {
this.triggerReadSelectedText();
// Speaks the first word.
this.mockTts.speakUntilCharIndex(10);
assertTrue(this.mockTts.currentlySpeaking());
assertEquals(this.mockTts.pendingUtterances().length, 1);
this.assertEqualsCollapseWhitespace(
this.mockTts.pendingUtterances()[0], 'Paragraph 1');
assertEquals(this.mockTts.getOptions().rate, 1.2);
// User-intiated pause.
selectToSpeak.onSelectToSpeakPanelAction_(
chrome.accessibilityPrivate.SelectToSpeakPanelAction.PAUSE);
assertFalse(this.mockTts.currentlySpeaking());
assertEquals(this.mockTts.pendingUtterances().length, 0);
// Changing speed will remain paused.
selectToSpeak.onSelectToSpeakPanelAction_(
chrome.accessibilityPrivate.SelectToSpeakPanelAction
.CHANGE_SPEED,
1.5);
// Wait an event loop so all pending promises are resolved prior to
// asserting that TTS remains paused.
setTimeout(this.newCallback(() => {
assertFalse(this.mockTts.currentlySpeaking());
assertEquals(this.mockTts.pendingUtterances().length, 0);
}, 0));
});
});
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