Reland: Dictation: Show toast when toggling Dictation fails
The original change was reverted because of failures on MSAN try bots: https://ci.chromium.org/ui/p/chromium/builders/ci/Linux%20ChromiumOS%20MSan%20Tests/37926/overview The test passes consistently using many parallel jobs on a local linux-chromeos-rel build, but I wasn't able to figure out a way to stabilize the test for MSAN. I will disable the test on MSAN for now, but added a TODO to address this before launching the new behavior. Original commit description: This change implements the behavior described in slide 7 of the UX mocks [1]. When Dictation fails to toggle because there is no focused text field, we now show a toast that explains why Dictation failed. To do this we: 1. Added accessibilityPrivate.showToast(), so that the dictation extension can request the toast to be shown. 2. Added AccessibilityNotificationController, which manages showing the toast. [1] https://docs.google.com/presentation/d/1mRnYcRpyzbY-EjzptCP3KRB5I7v_rM9Kl2zqhhQVZUY/edit#slide=id.g24cce1f4158_0_12 Bug: b:259352600 Change-Id: I3aac7437253aeb8423757038c763aa7ec10f7ccb Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4802964 Reviewed-by: Mark Schillaci <mschillaci@google.com> Reviewed-by: Alex Newcomer <newcomer@chromium.org> Reviewed-by: Xiyuan Xia <xiyuan@chromium.org> Commit-Queue: Akihiro Ota <akihiroota@chromium.org> Cr-Commit-Position: refs/heads/main@{#1186678}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
19dcafbc47
commit
e145b6bd3e
ash
BUILD.gn
accessibility
accessibility_controller_impl.ccaccessibility_controller_impl.haccessibility_controller_test_api_impl.ccaccessibility_controller_test_api_impl.haccessibility_notification_controller.ccaccessibility_notification_controller.h
ash_strings.grdash_strings_grd
constants
public
chrome
browser
accessibility
ash
resources
chromeos
accessibility
accessibility_common
common
ui
ash
common
extensions
extensions/browser
third_party/closure_compiler/externs
tools/metrics/histograms
@ -79,6 +79,8 @@ component("ash") {
|
||||
"accessibility/accessibility_delegate.h",
|
||||
"accessibility/accessibility_event_handler_manager.cc",
|
||||
"accessibility/accessibility_event_handler_manager.h",
|
||||
"accessibility/accessibility_notification_controller.cc",
|
||||
"accessibility/accessibility_notification_controller.h",
|
||||
"accessibility/accessibility_observer.h",
|
||||
"accessibility/autoclick/autoclick_controller.cc",
|
||||
"accessibility/autoclick/autoclick_controller.h",
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "ash/accelerators/accelerator_controller_impl.h"
|
||||
#include "ash/accessibility/a11y_feature_type.h"
|
||||
#include "ash/accessibility/accessibility_notification_controller.h"
|
||||
#include "ash/accessibility/accessibility_observer.h"
|
||||
#include "ash/accessibility/autoclick/autoclick_controller.h"
|
||||
#include "ash/accessibility/dictation_nudge_controller.h"
|
||||
@ -969,10 +970,14 @@ AccessibilityControllerImpl::AccessibilityControllerImpl()
|
||||
Shell::Get()->session_controller()->AddObserver(this);
|
||||
Shell::Get()->tablet_mode_controller()->AddObserver(this);
|
||||
CreateAccessibilityFeatures();
|
||||
|
||||
accessibility_notification_controller_ =
|
||||
std::make_unique<AccessibilityNotificationController>();
|
||||
}
|
||||
|
||||
AccessibilityControllerImpl::~AccessibilityControllerImpl() {
|
||||
floating_menu_controller_.reset();
|
||||
accessibility_notification_controller_.reset();
|
||||
}
|
||||
|
||||
void AccessibilityControllerImpl::CreateAccessibilityFeatures() {
|
||||
@ -1938,6 +1943,8 @@ void AccessibilityControllerImpl::OnDictationKeyboardDialogDismissed() {
|
||||
void AccessibilityControllerImpl::ShowDictationLanguageUpgradedNudge(
|
||||
const std::string& dictation_locale,
|
||||
const std::string& application_locale) {
|
||||
// TODO(b:259352600): Move dictation_nudge_controller_ into
|
||||
// accessibility_notification_controller.
|
||||
dictation_nudge_controller_ = std::make_unique<DictationNudgeController>(
|
||||
dictation_locale, application_locale);
|
||||
dictation_nudge_controller_->ShowNudge();
|
||||
@ -2925,4 +2932,14 @@ AccessibilityControllerImpl::GetDictationBubbleControllerForTest() {
|
||||
return dictation_bubble_controller_.get();
|
||||
}
|
||||
|
||||
void AccessibilityControllerImpl::ShowToast(AccessibilityToastType type) {
|
||||
accessibility_notification_controller_->ShowToast(type);
|
||||
}
|
||||
|
||||
void AccessibilityControllerImpl::AddShowToastCallbackForTesting(
|
||||
base::RepeatingClosure callback) {
|
||||
accessibility_notification_controller_->AddShowToastCallbackForTesting(
|
||||
std::move(callback));
|
||||
}
|
||||
|
||||
} // namespace ash
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "ash/accessibility/a11y_feature_type.h"
|
||||
#include "ash/accessibility/accessibility_notification_controller.h"
|
||||
#include "ash/ash_export.h"
|
||||
#include "ash/constants/ash_constants.h"
|
||||
#include "ash/public/cpp/accessibility_controller.h"
|
||||
@ -471,6 +472,7 @@ class ASH_EXPORT AccessibilityControllerImpl : public AccessibilityController,
|
||||
const absl::optional<std::vector<DictationBubbleHintType>>& hints)
|
||||
override;
|
||||
void SilenceSpokenFeedback() override;
|
||||
void ShowToast(AccessibilityToastType type) override;
|
||||
|
||||
// A confirmation dialog will be shown the first time an accessibility feature
|
||||
// is enabled using the specified accelerator key sequence. Only one dialog
|
||||
@ -534,6 +536,8 @@ class ASH_EXPORT AccessibilityControllerImpl : public AccessibilityController,
|
||||
OnDictationKeyboardDialogDismissed();
|
||||
}
|
||||
|
||||
void AddShowToastCallbackForTesting(base::RepeatingClosure callback);
|
||||
|
||||
private:
|
||||
// Populate |features_| with the feature of the correct type.
|
||||
void CreateAccessibilityFeatures();
|
||||
@ -647,6 +651,10 @@ class ASH_EXPORT AccessibilityControllerImpl : public AccessibilityController,
|
||||
// Used to control the Dictation bubble UI.
|
||||
std::unique_ptr<DictationBubbleController> dictation_bubble_controller_;
|
||||
|
||||
// Used to control accessibility-related notifications.
|
||||
std::unique_ptr<AccessibilityNotificationController>
|
||||
accessibility_notification_controller_;
|
||||
|
||||
// True if ChromeVox should enable its volume slide gesture.
|
||||
bool enable_chromevox_volume_slide_gesture_ = false;
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "ash/accelerators/accelerator_controller_impl.h"
|
||||
#include "ash/accessibility/accessibility_controller_impl.h"
|
||||
#include "ash/shell.h"
|
||||
#include "base/functional/callback.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
@ -52,6 +53,12 @@ void AccessibilityControllerTestApiImpl::DismissDictationKeyboardDialog() {
|
||||
->DismissDictationKeyboardDialogForTesting(); // IN-TEST
|
||||
}
|
||||
|
||||
void AccessibilityControllerTestApiImpl::AddShowToastCallbackForTesting(
|
||||
base::RepeatingClosure callback) const {
|
||||
GetController()->AddShowToastCallbackForTesting(
|
||||
std::move(callback)); // IN-TEST
|
||||
}
|
||||
|
||||
// static
|
||||
std::unique_ptr<AccessibilityControllerTestApi>
|
||||
AccessibilityControllerTestApi::Create() {
|
||||
|
@ -6,6 +6,7 @@
|
||||
#define ASH_ACCESSIBILITY_ACCESSIBILITY_CONTROLLER_TEST_API_IMPL_H_
|
||||
|
||||
#include "ash/public/cpp/test/accessibility_controller_test_api.h"
|
||||
#include "base/functional/callback_forward.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
@ -14,12 +15,10 @@ class AccessibilityControllerTestApiImpl
|
||||
: public AccessibilityControllerTestApi {
|
||||
public:
|
||||
AccessibilityControllerTestApiImpl();
|
||||
|
||||
AccessibilityControllerTestApiImpl(
|
||||
const AccessibilityControllerTestApiImpl&) = delete;
|
||||
AccessibilityControllerTestApiImpl& operator=(
|
||||
const AccessibilityControllerTestApiImpl&) = delete;
|
||||
|
||||
~AccessibilityControllerTestApiImpl() override;
|
||||
|
||||
// AccessibilityControllerTestApi:
|
||||
@ -29,6 +28,8 @@ class AccessibilityControllerTestApiImpl
|
||||
bool IsDictationKeboardDialogShowing() const override;
|
||||
void AcceptDictationKeyboardDialog() override;
|
||||
void DismissDictationKeyboardDialog() override;
|
||||
void AddShowToastCallbackForTesting(
|
||||
base::RepeatingClosure callback) const override;
|
||||
};
|
||||
|
||||
} // namespace ash
|
||||
|
56
ash/accessibility/accessibility_notification_controller.cc
Normal file
56
ash/accessibility/accessibility_notification_controller.cc
Normal file
@ -0,0 +1,56 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "ash/accessibility/accessibility_notification_controller.h"
|
||||
|
||||
#include "ash/public/cpp/accessibility_controller_enums.h"
|
||||
#include "ash/public/cpp/system/toast_data.h"
|
||||
#include "ash/shell.h"
|
||||
#include "ash/strings/grit/ash_strings.h"
|
||||
#include "ash/system/toast/toast_manager_impl.h"
|
||||
#include "ui/accessibility/accessibility_features.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
namespace {
|
||||
|
||||
const std::string kAccessibilityToastId = "AccessibilityToast";
|
||||
|
||||
ToastData GetToastData(AccessibilityToastType type) {
|
||||
switch (type) {
|
||||
case AccessibilityToastType::kDictationNoFocusedTextField:
|
||||
return {/*id=*/kAccessibilityToastId,
|
||||
/*catalog_name=*/ToastCatalogName::kDictationNoFocusedTextField,
|
||||
/*text=*/
|
||||
l10n_util::GetStringUTF16(
|
||||
IDS_ASH_ACCESSIBILITY_NUDGE_DICTATION_NO_FOCUSED_TEXT_FIELD)};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AccessibilityNotificationController::AccessibilityNotificationController() =
|
||||
default;
|
||||
AccessibilityNotificationController::~AccessibilityNotificationController() =
|
||||
default;
|
||||
|
||||
void AccessibilityNotificationController::ShowToast(
|
||||
AccessibilityToastType type) {
|
||||
if (!::features::IsAccessibilityDictationKeyboardImprovementsEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Shell::Get()->toast_manager()->Show(GetToastData(type));
|
||||
if (show_anchored_nudge_callback_for_testing_) {
|
||||
show_anchored_nudge_callback_for_testing_.Run();
|
||||
}
|
||||
}
|
||||
|
||||
void AccessibilityNotificationController::AddShowToastCallbackForTesting(
|
||||
base::RepeatingClosure callback) {
|
||||
show_anchored_nudge_callback_for_testing_ = std::move(callback);
|
||||
}
|
||||
|
||||
} // namespace ash
|
33
ash/accessibility/accessibility_notification_controller.h
Normal file
33
ash/accessibility/accessibility_notification_controller.h
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ASH_ACCESSIBILITY_ACCESSIBILITY_NOTIFICATION_CONTROLLER_H_
|
||||
#define ASH_ACCESSIBILITY_ACCESSIBILITY_NOTIFICATION_CONTROLLER_H_
|
||||
|
||||
#include "ash/ash_export.h"
|
||||
#include "ash/public/cpp/accessibility_controller_enums.h"
|
||||
#include "base/functional/callback.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
// Class that manages showing notifications for accessibility.
|
||||
class ASH_EXPORT AccessibilityNotificationController {
|
||||
public:
|
||||
AccessibilityNotificationController();
|
||||
AccessibilityNotificationController(
|
||||
const AccessibilityNotificationController&) = delete;
|
||||
AccessibilityNotificationController& operator=(
|
||||
const AccessibilityNotificationController&) = delete;
|
||||
~AccessibilityNotificationController();
|
||||
|
||||
void ShowToast(AccessibilityToastType type);
|
||||
void AddShowToastCallbackForTesting(base::RepeatingClosure callback);
|
||||
|
||||
private:
|
||||
base::RepeatingClosure show_anchored_nudge_callback_for_testing_;
|
||||
};
|
||||
|
||||
} // namespace ash
|
||||
|
||||
#endif // ASH_ACCESSIBILITY_ACCESSIBILITY_NOTIFICATION_CONTROLLER_H_
|
@ -1333,6 +1333,11 @@ Style notes:
|
||||
With dictation, you can type using your voice. Press the dictation key or select the microphone icon at the bottom of the screen when you’re on a text field. Your dictation language is set to <ph name="language">$1<ex>English (United States)</ex></ph>. Speech is sent to Google for processing. You can change the dictation language anytime in Settings > Accessibility.
|
||||
</message>
|
||||
|
||||
<!-- Accessibility nudges -->
|
||||
<message name="IDS_ASH_ACCESSIBILITY_NUDGE_DICTATION_NO_FOCUSED_TEXT_FIELD" desc="Displayed in a nudge when Dictation is stopped automatically because there is no focused text field.">
|
||||
Go to a text field to use Dictation
|
||||
</message>
|
||||
|
||||
<message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_VIRTUAL_KEYBOARD" desc="The label used in the accessibility menu of the system tray to toggle on/off the onscreen keyboard.">
|
||||
On-screen keyboard
|
||||
</message>
|
||||
|
1
ash/ash_strings_grd/IDS_ASH_ACCESSIBILITY_NUDGE_DICTATION_NO_FOCUSED_TEXT_FIELD.png.sha1
Normal file
1
ash/ash_strings_grd/IDS_ASH_ACCESSIBILITY_NUDGE_DICTATION_NO_FOCUSED_TEXT_FIELD.png.sha1
Normal file
@ -0,0 +1 @@
|
||||
cc8d39b5868c8f202c7edc14eedd27e7e64855dd
|
@ -273,7 +273,8 @@ enum class ToastCatalogName {
|
||||
kCopyGifToClipboardAction = 42,
|
||||
// [Deprecated] kVideoConferenceTrayUseWhileDisabled = 43,
|
||||
kBatterySaverDisabled = 44,
|
||||
kMaxValue = kBatterySaverDisabled
|
||||
kDictationNoFocusedTextField = 45,
|
||||
kMaxValue = kDictationNoFocusedTextField
|
||||
};
|
||||
|
||||
} // namespace ash
|
||||
|
@ -210,6 +210,9 @@ class ASH_PUBLIC_EXPORT AccessibilityController {
|
||||
// Cancels all of spoken feedback's current and queued speech immediately.
|
||||
virtual void SilenceSpokenFeedback() = 0;
|
||||
|
||||
// Shows an accessibility-related toast.
|
||||
virtual void ShowToast(AccessibilityToastType type) = 0;
|
||||
|
||||
protected:
|
||||
AccessibilityController();
|
||||
virtual ~AccessibilityController();
|
||||
|
@ -232,6 +232,12 @@ enum class DictationNotificationType {
|
||||
kOnlyPumpkinDownloaded,
|
||||
};
|
||||
|
||||
// The types of accessibility-related toasts. This enum should be kept in sync
|
||||
// with chrome.accessibilityPrivate.ToastType.
|
||||
enum class AccessibilityToastType {
|
||||
kDictationNoFocusedTextField,
|
||||
};
|
||||
|
||||
} // namespace ash
|
||||
|
||||
#endif // ASH_PUBLIC_CPP_ACCESSIBILITY_CONTROLLER_ENUMS_H_
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "ash/ash_export.h"
|
||||
#include "base/functional/callback_forward.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
@ -27,6 +28,8 @@ class ASH_EXPORT AccessibilityControllerTestApi {
|
||||
virtual bool IsDictationKeboardDialogShowing() const = 0;
|
||||
virtual void AcceptDictationKeyboardDialog() = 0;
|
||||
virtual void DismissDictationKeyboardDialog() = 0;
|
||||
virtual void AddShowToastCallbackForTesting(
|
||||
base::RepeatingClosure callback) const = 0;
|
||||
};
|
||||
|
||||
} // namespace ash
|
||||
|
@ -67,6 +67,17 @@ namespace {
|
||||
namespace accessibility_private = ::extensions::api::accessibility_private;
|
||||
using ::ash::AccessibilityManager;
|
||||
|
||||
ash::AccessibilityToastType ConvertToastType(
|
||||
accessibility_private::ToastType type) {
|
||||
switch (type) {
|
||||
case accessibility_private::ToastType::
|
||||
TOAST_TYPE_DICTATIONNOFOCUSEDTEXTFIELD:
|
||||
return ash::AccessibilityToastType::kDictationNoFocusedTextField;
|
||||
case accessibility_private::ToastType::TOAST_TYPE_NONE:
|
||||
NOTREACHED_NORETURN();
|
||||
}
|
||||
}
|
||||
|
||||
ash::DictationBubbleHintType ConvertDictationHintType(
|
||||
accessibility_private::DictationBubbleHintType hint_type) {
|
||||
switch (hint_type) {
|
||||
@ -782,6 +793,15 @@ AccessibilityPrivateSetVirtualKeyboardVisibleFunction::Run() {
|
||||
return RespondNow(NoArguments());
|
||||
}
|
||||
|
||||
ExtensionFunction::ResponseAction AccessibilityPrivateShowToastFunction::Run() {
|
||||
absl::optional<accessibility_private::ShowToast::Params> params(
|
||||
accessibility_private::ShowToast::Params::Create(args()));
|
||||
EXTENSION_FUNCTION_VALIDATE(params);
|
||||
ash::AccessibilityController::Get()->ShowToast(
|
||||
ConvertToastType(params->type));
|
||||
return RespondNow(NoArguments());
|
||||
}
|
||||
|
||||
ExtensionFunction::ResponseAction
|
||||
AccessibilityPrivateShowConfirmationDialogFunction::Run() {
|
||||
absl::optional<accessibility_private::ShowConfirmationDialog::Params> params =
|
||||
|
@ -240,6 +240,14 @@ class AccessibilityPrivateSetVirtualKeyboardVisibleFunction
|
||||
ACCESSIBILITY_PRIVATE_SETVIRTUALKEYBOARDVISIBLE)
|
||||
};
|
||||
|
||||
// API function that displays an accessibility-related toast.
|
||||
class AccessibilityPrivateShowToastFunction : public ExtensionFunction {
|
||||
~AccessibilityPrivateShowToastFunction() override = default;
|
||||
ResponseAction Run() override;
|
||||
DECLARE_EXTENSION_FUNCTION("accessibilityPrivate.showToast",
|
||||
ACCESSIBILITY_PRIVATE_SHOWTOAST)
|
||||
};
|
||||
|
||||
// API function that shows a confirmation dialog, with callbacks for
|
||||
// confirm/cancel.
|
||||
class AccessibilityPrivateShowConfirmationDialogFunction
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "ash/constants/ash_pref_names.h"
|
||||
#include "ash/public/cpp/system_tray_test_api.h"
|
||||
#include "ash/public/cpp/test/accessibility_controller_test_api.h"
|
||||
#include "ash/root_window_controller.h"
|
||||
#include "ash/shell.h"
|
||||
#include "ash/system/accessibility/dictation_button_tray.h"
|
||||
@ -316,6 +317,8 @@ class DictationTestBase : public InProcessBrowserTest,
|
||||
utils_->set_wait_for_accessibility_common_extension_load_(use);
|
||||
}
|
||||
|
||||
DictationTestUtils* utils() { return utils_.get(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<DictationTestUtils> utils_;
|
||||
base::test::ScopedFeatureList scoped_feature_list_;
|
||||
@ -2240,4 +2243,109 @@ IN_PROC_BROWSER_TEST_P(DictationFormattedContentEditableTest,
|
||||
SendFinalResultAndWaitForEditableValue("was one", "This was one test");
|
||||
}
|
||||
|
||||
class AccessibilityToastCallbackManager {
|
||||
public:
|
||||
void OnToastShown() { run_loop_.Quit(); }
|
||||
void WaitForToastShown() { run_loop_.Run(); }
|
||||
|
||||
private:
|
||||
base::RunLoop run_loop_;
|
||||
};
|
||||
|
||||
class DictationKeyboardImprovementsTest : public DictationTestBase {
|
||||
public:
|
||||
DictationKeyboardImprovementsTest() = default;
|
||||
~DictationKeyboardImprovementsTest() override = default;
|
||||
DictationKeyboardImprovementsTest(const DictationKeyboardImprovementsTest&) =
|
||||
delete;
|
||||
DictationKeyboardImprovementsTest& operator=(
|
||||
const DictationKeyboardImprovementsTest&) = delete;
|
||||
|
||||
protected:
|
||||
void SetUpCommandLine(base::CommandLine* command_line) override {
|
||||
DictationTestBase::SetUpCommandLine(command_line);
|
||||
|
||||
std::vector<base::test::FeatureRef> enabled_features{
|
||||
::features::kAccessibilityDictationKeyboardImprovements};
|
||||
scoped_feature_list_.InitWithFeatures(
|
||||
enabled_features, std::vector<base::test::FeatureRef>());
|
||||
}
|
||||
|
||||
void SetUpOnMainThread() override {
|
||||
DictationTestBase::SetUpOnMainThread();
|
||||
test_api_ = AccessibilityControllerTestApi::Create();
|
||||
callback_manager_ = std::make_unique<AccessibilityToastCallbackManager>();
|
||||
test_api_->AddShowToastCallbackForTesting(
|
||||
base::BindRepeating(&AccessibilityToastCallbackManager::OnToastShown,
|
||||
base::Unretained(callback_manager_.get())));
|
||||
|
||||
// Tab away from the editable.
|
||||
ASSERT_NO_FATAL_FAILURE(ASSERT_TRUE(ui_test_utils::SendKeyPressToWindowSync(
|
||||
/*window=*/nullptr, /*key=*/ui::KeyboardCode::VKEY_TAB,
|
||||
/*control=*/false,
|
||||
/*shift=*/false, /*alt=*/false, /*command=*/false)));
|
||||
// Reduce the no focused IME timeout so that the nudge will be shown
|
||||
// promptly.
|
||||
ExecuteAccessibilityCommonScript(
|
||||
"testSupport.setNoFocusedImeTimeout(500);");
|
||||
}
|
||||
|
||||
void WaitForToastShown() { callback_manager_->WaitForToastShown(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<AccessibilityControllerTestApi> test_api_;
|
||||
std::unique_ptr<AccessibilityToastCallbackManager> callback_manager_;
|
||||
base::test::ScopedFeatureList scoped_feature_list_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
NetworkDictationKeyboardImprovementsTest,
|
||||
DictationKeyboardImprovementsTest,
|
||||
::testing::Values(TestConfig(speech::SpeechRecognitionType::kNetwork,
|
||||
EditableType::kInput)));
|
||||
|
||||
// Verifies that a nudge is shown in the system UI when Dictation is toggled
|
||||
// when there is no focused editable.
|
||||
IN_PROC_BROWSER_TEST_P(DictationKeyboardImprovementsTest,
|
||||
ToggledWithNoFocusShowsNudge) {
|
||||
// Disable the console observer because toggling Dictation in the following
|
||||
// manner will cause an error to be emitted to the console.
|
||||
utils()->DisableConsoleObserver();
|
||||
ToggleDictationWithKeystroke();
|
||||
WaitForToastShown();
|
||||
WaitForRecognitionStopped();
|
||||
}
|
||||
|
||||
// Verifies that ChromeVox announces a message when when Dictation is toggled
|
||||
// when there is no focused editable.
|
||||
// TODO(b:259352600): Re-enable this test on MSAN.
|
||||
#if defined(MEMORY_SANITIZER)
|
||||
#define MAYBE_ToggledWithNoFocusTriggersSpeech \
|
||||
DISABLED_ToggledWithNoFocusTriggersSpeech
|
||||
#else
|
||||
#define MAYBE_ToggledWithNoFocusTriggersSpeech ToggledWithNoFocusTriggersSpeech
|
||||
#endif
|
||||
IN_PROC_BROWSER_TEST_P(DictationKeyboardImprovementsTest,
|
||||
MAYBE_ToggledWithNoFocusTriggersSpeech) {
|
||||
// Setup ChromeVox.
|
||||
test::SpeechMonitor sm;
|
||||
EXPECT_FALSE(GetManager()->IsSpokenFeedbackEnabled());
|
||||
extensions::ExtensionHostTestHelper host_helper(
|
||||
browser()->profile(), extension_misc::kChromeVoxExtensionId);
|
||||
EnableChromeVox();
|
||||
host_helper.WaitForHostCompletedFirstLoad();
|
||||
EXPECT_TRUE(GetManager()->IsSpokenFeedbackEnabled());
|
||||
|
||||
// Disable the console observer because toggling Dictation in the following
|
||||
// manner will cause an error to be emitted to the console.
|
||||
utils()->DisableConsoleObserver();
|
||||
ToggleDictationWithKeystroke();
|
||||
WaitForToastShown();
|
||||
WaitForRecognitionStopped();
|
||||
|
||||
// Assert speech from ChromeVox.
|
||||
sm.ExpectSpeechPattern("*Go to a text field to use Dictation*");
|
||||
sm.Replay();
|
||||
}
|
||||
|
||||
} // namespace ash
|
||||
|
@ -179,7 +179,7 @@ void DictationTestUtils::EnableDictation(Browser* browser) {
|
||||
|
||||
// Increase Dictation's NO_FOCUSED_IME timeout to reduce flakiness on slower
|
||||
// builds.
|
||||
std::string script = "testSupport.increaseNoFocusedImeTimeout();";
|
||||
std::string script = "testSupport.setNoFocusedImeTimeout(20 * 1000);";
|
||||
ExecuteAccessibilityCommonScript(script);
|
||||
|
||||
// Dictation will request a Pumpkin install when it starts up. Wait for
|
||||
|
@ -94,6 +94,10 @@ class DictationTestUtils {
|
||||
int GetCommitTextCallCount();
|
||||
void WaitForCommitText(const std::u16string& value);
|
||||
|
||||
// TODO(b:259352600): Instead of disabling the observer, change this to
|
||||
// allow specific messages.
|
||||
void DisableConsoleObserver() { console_observer_.reset(); }
|
||||
|
||||
// Sets whether or not we should wait for the accessibility common extension
|
||||
// to load when enabling Dictation. This should be true in almost all cases.
|
||||
// However, there are times when we don't want to wait for accessibility
|
||||
|
@ -202,7 +202,7 @@ export class Dictation {
|
||||
}
|
||||
this.setStopTimeout_(
|
||||
Dictation.Timeouts.NO_FOCUSED_IME_MS,
|
||||
'Dictation stopped automatically: No focused IME');
|
||||
Dictation.StopReason.NO_FOCUSED_IME);
|
||||
this.inputController_.connect(() => this.maybeStartSpeechRecognition_());
|
||||
}
|
||||
|
||||
@ -262,18 +262,21 @@ export class Dictation {
|
||||
/**
|
||||
* Sets the timeout to stop Dictation.
|
||||
* @param {number} durationMs
|
||||
* @param {string=} debugInfo Optional debugging information for why Dictation
|
||||
* @param {Dictation.StopReason=} reason Optional reason for why Dictation
|
||||
* stopped automatically.
|
||||
* @private
|
||||
*/
|
||||
setStopTimeout_(durationMs, debugInfo) {
|
||||
setStopTimeout_(durationMs, reason) {
|
||||
if (this.stopTimeoutId_ !== null) {
|
||||
clearTimeout(this.stopTimeoutId_);
|
||||
}
|
||||
this.stopTimeoutId_ = setTimeout(() => {
|
||||
this.stopDictation_(/*notify=*/ true);
|
||||
if (debugInfo) {
|
||||
console.log(debugInfo);
|
||||
|
||||
if (reason === Dictation.StopReason.NO_FOCUSED_IME) {
|
||||
chrome.accessibilityPrivate.showToast(
|
||||
chrome.accessibilityPrivate.ToastType
|
||||
.DICTATION_NO_FOCUSED_TEXT_FIELD);
|
||||
}
|
||||
}, durationMs);
|
||||
}
|
||||
@ -540,12 +543,9 @@ export class Dictation {
|
||||
InputController.IME_ENGINE_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to increase the NO_FOCUSED_IME_MS timeout to reduce the flakiness of
|
||||
* Dictation tests on slower builds. For testing purposes only.
|
||||
*/
|
||||
increaseNoFocusedImeTimeoutForTesting() {
|
||||
Dictation.Timeouts.NO_FOCUSED_IME_MS = 20 * 1000;
|
||||
/** Used to set the NO_FOCUSED_IME_MS timeout for testing purposes only. */
|
||||
setNoFocusedImeTimeoutForTesting(duration) {
|
||||
Dictation.Timeouts.NO_FOCUSED_IME_MS = duration;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -628,3 +628,8 @@ Dictation.Timeouts = {
|
||||
NO_NEW_SPEECH_MS: 5 * 1000,
|
||||
NO_FOCUSED_IME_MS: 1000,
|
||||
};
|
||||
|
||||
/** @enum {string} */
|
||||
Dictation.StopReason = {
|
||||
NO_FOCUSED_IME: 'Dictation stopped automatically: No focused IME',
|
||||
};
|
||||
|
@ -116,7 +116,7 @@ DictationE2ETestBase = class extends E2ETestBase {
|
||||
accessibilityCommon.dictation_.disablePumpkinForTesting();
|
||||
// Increase Dictation's NO_FOCUSED_IME timeout to reduce flakiness on slower
|
||||
// builds.
|
||||
accessibilityCommon.dictation_.increaseNoFocusedImeTimeoutForTesting();
|
||||
accessibilityCommon.dictation_.setNoFocusedImeTimeoutForTesting(20 * 1000);
|
||||
}
|
||||
|
||||
/** @override */
|
||||
|
@ -20,9 +20,9 @@ class DictationTestSupport {
|
||||
domAutomationController.send('ready');
|
||||
}
|
||||
|
||||
/** Increases Dictation timeouts for test stability. */
|
||||
increaseNoFocusedImeTimeout() {
|
||||
this.dictation_.increaseNoFocusedImeTimeoutForTesting();
|
||||
/** Sets Dictation timeouts for test stability. */
|
||||
setNoFocusedImeTimeout(duration) {
|
||||
this.dictation_.setNoFocusedImeTimeoutForTesting(duration);
|
||||
this.notifyCcTests_();
|
||||
}
|
||||
|
||||
|
@ -73,6 +73,10 @@ class MockAccessibilityPrivate {
|
||||
|
||||
this.SyntheticKeyboardEventType = {KEYDOWN: 'keydown', KEYUP: 'keyup'};
|
||||
|
||||
this.ToastType = {
|
||||
DICTATION_NO_FOCUSED_TEXT_FIELD: 'dictationNoFocusedTextField',
|
||||
};
|
||||
|
||||
/** @private {function<number, number>} */
|
||||
this.boundsListener_ = null;
|
||||
|
||||
@ -484,4 +488,7 @@ class MockAccessibilityPrivate {
|
||||
await getFileBytes(`${pumpkinDir}/es_es/pumpkin_config.binarypb`);
|
||||
MockAccessibilityPrivate.pumpkinData_ = data;
|
||||
}
|
||||
|
||||
/** @param {!chrome.accessibilityPrivate.ToastType} type */
|
||||
showToast(type) {}
|
||||
}
|
||||
|
@ -117,3 +117,5 @@ void FakeAccessibilityController::UpdateDictationBubble(
|
||||
const absl::optional<std::vector<ash::DictationBubbleHintType>>& hints) {}
|
||||
|
||||
void FakeAccessibilityController::SilenceSpokenFeedback() {}
|
||||
|
||||
void FakeAccessibilityController::ShowToast(ash::AccessibilityToastType type) {}
|
||||
|
@ -77,6 +77,7 @@ class FakeAccessibilityController : ash::AccessibilityController {
|
||||
const absl::optional<std::vector<ash::DictationBubbleHintType>>& hints)
|
||||
override;
|
||||
void SilenceSpokenFeedback() override;
|
||||
void ShowToast(ash::AccessibilityToastType type) override;
|
||||
|
||||
private:
|
||||
bool was_client_set_ = false;
|
||||
|
@ -292,6 +292,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "ToastType",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"dictationNoFocusedTextField"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "DlcType",
|
||||
"type": "string",
|
||||
@ -894,6 +901,16 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "showToast",
|
||||
"type": "function",
|
||||
"description": "Displays an accessibility-related toast.",
|
||||
"parameters": [{
|
||||
"name": "type",
|
||||
"$ref": "ToastType",
|
||||
"description": "The type of toast to show."
|
||||
}]
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
|
@ -1880,6 +1880,7 @@ enum HistogramValue {
|
||||
OS_DIAGNOSTICS_RUNBLUETOOTHSCANNINGROUTINE = 1818,
|
||||
OS_DIAGNOSTICS_RUNBLUETOOTHPAIRINGROUTINE = 1819,
|
||||
FILEMANAGERPRIVATE_DISMISSIOTASK = 1820,
|
||||
ACCESSIBILITY_PRIVATE_SHOWTOAST = 1821,
|
||||
// Last entry: Add new entries above, then run:
|
||||
// tools/metrics/histograms/update_extension_histograms.py
|
||||
ENUM_BOUNDARY
|
||||
|
@ -339,6 +339,13 @@ chrome.accessibilityPrivate.DictationBubbleHintType = {
|
||||
*/
|
||||
chrome.accessibilityPrivate.DictationBubbleProperties;
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
*/
|
||||
chrome.accessibilityPrivate.ToastType = {
|
||||
DICTATION_NO_FOCUSED_TEXT_FIELD: 'dictationNoFocusedTextField',
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
*/
|
||||
@ -657,6 +664,13 @@ chrome.accessibilityPrivate.getDlcContents = function(dlc, callback) {};
|
||||
*/
|
||||
chrome.accessibilityPrivate.isLacrosPrimary = function(callback) {};
|
||||
|
||||
/**
|
||||
* Displays an accessibility-related toast.
|
||||
* @param {!chrome.accessibilityPrivate.ToastType} type The type of toast to
|
||||
* show.
|
||||
*/
|
||||
chrome.accessibilityPrivate.showToast = function(type) {};
|
||||
|
||||
/**
|
||||
* Fired whenever ChromeVox should output introduction.
|
||||
* @type {!ChromeEvent}
|
||||
|
@ -37589,6 +37589,7 @@ Called by update_extension_histograms.py.-->
|
||||
<int value="1818" label="OS_DIAGNOSTICS_RUNBLUETOOTHSCANNINGROUTINE"/>
|
||||
<int value="1819" label="OS_DIAGNOSTICS_RUNBLUETOOTHPAIRINGROUTINE"/>
|
||||
<int value="1820" label="FILEMANAGERPRIVATE_DISMISSIOTASK"/>
|
||||
<int value="1821" label="ACCESSIBILITY_PRIVATE_SHOWTOAST"/>
|
||||
</enum>
|
||||
|
||||
<enum name="ExtensionIconState">
|
||||
@ -77776,6 +77777,8 @@ Called by update_net_trust_anchors.py.-->
|
||||
<int value="18" label="Scalable IPH Bubble"/>
|
||||
<int value="19"
|
||||
label="Video Conference Tray Camera And Microphone Use While Disabled"/>
|
||||
<int value="20" label="Multitask Menu Clamshell"/>
|
||||
<int value="21" label="Multitask Menu Tablet"/>
|
||||
</enum>
|
||||
|
||||
<enum name="NukeProfileResult">
|
||||
@ -104470,6 +104473,7 @@ would be helpful to identify which type is being sent.
|
||||
<int value="41" label="Video Conference Tray Speak-On-Mute Detected"/>
|
||||
<int value="42" label="Copy Gif To Clipboard Action"/>
|
||||
<int value="44" label="Battery Saver Disabled"/>
|
||||
<int value="45" label="Dictation No Focused Text Field"/>
|
||||
</enum>
|
||||
|
||||
<enum name="TokenBinding.KeyMatch">
|
||||
|
Reference in New Issue
Block a user