AccessibilityCommon extension is an IME when Dictation is activated.
Dictation adds AccessibilityCommon extension as an IME when Dictation is activated, and removes itself when Dictation is deactivated. Design: go/cros-dictation-extension Test: dictation_test.js, manual testing Bug: 1216111 Change-Id: Ic6bd9d53bda5bb3e863153cb7ec3c4c4274460e7 AX-Relnotes: N/A Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2945333 Reviewed-by: dpapad <dpapad@chromium.org> Reviewed-by: Tetsui Ohkubo <tetsui@chromium.org> Reviewed-by: Xiyuan Xia <xiyuan@chromium.org> Reviewed-by: My Nguyen <myy@chromium.org> Reviewed-by: Matthew Wolenetz <wolenetz@chromium.org> Reviewed-by: Akihiro Ota <akihiroota@chromium.org> Commit-Queue: Katie Dektar <katie@chromium.org> Cr-Commit-Position: refs/heads/master@{#893100}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
1518adb0ef
commit
9a44cc619c
ash
accelerators
ime
login
system
chrome/browser
media
resources
chromeos
accessibility
settings
chromeos
os_languages_page
languages_page
@ -143,13 +143,27 @@ void AddTestImes() {
|
||||
ime1.id = "id1";
|
||||
ImeInfo ime2;
|
||||
ime2.id = "id2";
|
||||
std::vector<ImeInfo> available_imes;
|
||||
available_imes.push_back(std::move(ime1));
|
||||
available_imes.push_back(std::move(ime2));
|
||||
Shell::Get()->ime_controller()->RefreshIme("id1", std::move(available_imes),
|
||||
std::vector<ImeInfo> visible_imes;
|
||||
visible_imes.push_back(std::move(ime1));
|
||||
visible_imes.push_back(std::move(ime2));
|
||||
Shell::Get()->ime_controller()->RefreshIme("id1", std::move(visible_imes),
|
||||
std::vector<ImeMenuItem>());
|
||||
}
|
||||
|
||||
void AddNotVisibleTestIme() {
|
||||
ImeInfo dictation;
|
||||
dictation.id = "_ext_ime_egfdjlfmgnehecnclamagfafdccgfndpdictation";
|
||||
const std::vector<ImeInfo> visible_imes =
|
||||
Shell::Get()->ime_controller()->GetVisibleImes();
|
||||
std::vector<ImeInfo> available_imes;
|
||||
for (auto ime : visible_imes) {
|
||||
available_imes.push_back(ime);
|
||||
}
|
||||
available_imes.push_back(dictation);
|
||||
Shell::Get()->ime_controller()->RefreshIme(
|
||||
dictation.id, std::move(available_imes), std::vector<ImeMenuItem>());
|
||||
}
|
||||
|
||||
ui::Accelerator CreateReleaseAccelerator(ui::KeyboardCode key_code,
|
||||
int modifiers) {
|
||||
ui::Accelerator accelerator(key_code, modifiers);
|
||||
@ -1426,7 +1440,7 @@ TEST_F(AcceleratorControllerTest, GlobalAcceleratorsToggleAppListFullscreen) {
|
||||
}
|
||||
|
||||
TEST_F(AcceleratorControllerTest, ImeGlobalAccelerators) {
|
||||
ASSERT_EQ(0u, Shell::Get()->ime_controller()->available_imes().size());
|
||||
ASSERT_EQ(0u, Shell::Get()->ime_controller()->GetVisibleImes().size());
|
||||
|
||||
// Cycling IME is blocked because there is nothing to switch to.
|
||||
ui::Accelerator control_space_down(ui::VKEY_SPACE, ui::EF_CONTROL_DOWN);
|
||||
@ -1438,11 +1452,24 @@ TEST_F(AcceleratorControllerTest, ImeGlobalAccelerators) {
|
||||
EXPECT_FALSE(ProcessInController(control_space_up));
|
||||
EXPECT_FALSE(ProcessInController(control_shift_space));
|
||||
|
||||
// Adding only a not visible IME doesn't make IME accelerators available.
|
||||
AddNotVisibleTestIme();
|
||||
ASSERT_EQ(0u, Shell::Get()->ime_controller()->GetVisibleImes().size());
|
||||
EXPECT_FALSE(ProcessInController(control_space_down));
|
||||
EXPECT_FALSE(ProcessInController(control_space_up));
|
||||
EXPECT_FALSE(ProcessInController(control_shift_space));
|
||||
|
||||
// Cycling IME works when there are IMEs available.
|
||||
AddTestImes();
|
||||
EXPECT_TRUE(ProcessInController(control_space_down));
|
||||
EXPECT_TRUE(ProcessInController(control_space_up));
|
||||
EXPECT_TRUE(ProcessInController(control_shift_space));
|
||||
|
||||
// Adding the not visible IME back doesn't block cycling.
|
||||
AddNotVisibleTestIme();
|
||||
EXPECT_TRUE(ProcessInController(control_space_down));
|
||||
EXPECT_TRUE(ProcessInController(control_space_up));
|
||||
EXPECT_TRUE(ProcessInController(control_shift_space));
|
||||
}
|
||||
|
||||
// TODO(nona|mazda): Remove this when crbug.com/139556 in a better way.
|
||||
|
@ -31,6 +31,10 @@ enum class ModeChangeKeyAction {
|
||||
kMaxValue = kSwitchIme
|
||||
};
|
||||
|
||||
// The ID for the Accessibility Common IME (used for Dictation).
|
||||
const char* kAccessibilityCommonIMEId =
|
||||
"_ext_ime_egfdjlfmgnehecnclamagfafdccgfndpdictation";
|
||||
|
||||
} // namespace
|
||||
|
||||
ImeControllerImpl::ImeControllerImpl()
|
||||
@ -48,6 +52,14 @@ void ImeControllerImpl::RemoveObserver(Observer* observer) {
|
||||
observers_.RemoveObserver(observer);
|
||||
}
|
||||
|
||||
const std::vector<ImeInfo>& ImeControllerImpl::GetVisibleImes() const {
|
||||
return visible_imes_;
|
||||
}
|
||||
|
||||
bool ImeControllerImpl::IsCurrentImeVisible() const {
|
||||
return current_ime_.id != kAccessibilityCommonIMEId;
|
||||
}
|
||||
|
||||
void ImeControllerImpl::SetClient(ImeControllerClient* client) {
|
||||
if (client_) {
|
||||
if (CastConfigController::Get())
|
||||
@ -71,7 +83,7 @@ bool ImeControllerImpl::CanSwitchIme() const {
|
||||
|
||||
// Do not consume key event if there is only one input method is enabled.
|
||||
// Ctrl+Space or Alt+Shift may be used by other application.
|
||||
return available_imes_.size() > 1;
|
||||
return GetVisibleImes().size() > 1;
|
||||
}
|
||||
|
||||
void ImeControllerImpl::SwitchToNextIme() {
|
||||
@ -131,12 +143,17 @@ void ImeControllerImpl::RefreshIme(const std::string& current_ime_id,
|
||||
|
||||
available_imes_.clear();
|
||||
available_imes_.reserve(available_imes.size());
|
||||
visible_imes_.clear();
|
||||
visible_imes_.reserve(visible_imes_.size());
|
||||
for (const auto& ime : available_imes) {
|
||||
if (ime.id.empty()) {
|
||||
DLOG(ERROR) << "Received IME with invalid ID.";
|
||||
continue;
|
||||
}
|
||||
available_imes_.push_back(ime);
|
||||
if (ime.id != kAccessibilityCommonIMEId) {
|
||||
visible_imes_.push_back(ime);
|
||||
}
|
||||
if (ime.id == current_ime_id)
|
||||
current_ime_ = ime;
|
||||
}
|
||||
|
@ -52,9 +52,10 @@ class ASH_EXPORT ImeControllerImpl : public ImeController,
|
||||
void AddObserver(Observer* observer);
|
||||
void RemoveObserver(Observer* observer);
|
||||
|
||||
const ImeInfo& current_ime() const { return current_ime_; }
|
||||
const std::vector<ImeInfo>& GetVisibleImes() const;
|
||||
bool IsCurrentImeVisible() const;
|
||||
|
||||
const std::vector<ImeInfo>& available_imes() const { return available_imes_; }
|
||||
const ImeInfo& current_ime() const { return current_ime_; }
|
||||
|
||||
bool is_extra_input_options_enabled() const {
|
||||
return is_extra_input_options_enabled_;
|
||||
@ -147,6 +148,10 @@ class ASH_EXPORT ImeControllerImpl : public ImeController,
|
||||
// "Available" IMEs are both installed and enabled by the user in settings.
|
||||
std::vector<ImeInfo> available_imes_;
|
||||
|
||||
// "Visible" IMEs are installed, enabled, and don't include built-in IMEs that
|
||||
// shouldn't be shown to the user, like Dictation.
|
||||
std::vector<ImeInfo> visible_imes_;
|
||||
|
||||
// True if the available IMEs are currently managed by enterprise policy.
|
||||
// For example, can occur at the login screen with device-level policy.
|
||||
bool managed_by_policy_ = false;
|
||||
|
@ -102,9 +102,9 @@ TEST_F(ImeControllerImplTest, RefreshIme) {
|
||||
|
||||
// Cached data was updated.
|
||||
EXPECT_EQ("ime1", controller->current_ime().id);
|
||||
ASSERT_EQ(2u, controller->available_imes().size());
|
||||
EXPECT_EQ("ime1", controller->available_imes()[0].id);
|
||||
EXPECT_EQ("ime2", controller->available_imes()[1].id);
|
||||
ASSERT_EQ(2u, controller->GetVisibleImes().size());
|
||||
EXPECT_EQ("ime1", controller->GetVisibleImes()[0].id);
|
||||
EXPECT_EQ("ime2", controller->GetVisibleImes()[1].id);
|
||||
ASSERT_EQ(1u, controller->current_ime_menu_items().size());
|
||||
EXPECT_EQ("menu1", controller->current_ime_menu_items()[0].key);
|
||||
|
||||
@ -118,6 +118,7 @@ TEST_F(ImeControllerImplTest, NoCurrentIme) {
|
||||
// Set up a single IME.
|
||||
RefreshImes("ime1", {"ime1"});
|
||||
EXPECT_EQ("ime1", controller->current_ime().id);
|
||||
EXPECT_TRUE(controller->IsCurrentImeVisible());
|
||||
|
||||
// When there is no current IME the cached current IME is empty.
|
||||
const std::string empty_ime_id;
|
||||
@ -125,6 +126,30 @@ TEST_F(ImeControllerImplTest, NoCurrentIme) {
|
||||
EXPECT_TRUE(controller->current_ime().id.empty());
|
||||
}
|
||||
|
||||
TEST_F(ImeControllerImplTest, CurrentImeNotVisible) {
|
||||
ImeControllerImpl* controller = Shell::Get()->ime_controller();
|
||||
|
||||
// Add only Dictation.
|
||||
std::string dictation_id =
|
||||
"_ext_ime_egfdjlfmgnehecnclamagfafdccgfndpdictation";
|
||||
RefreshImes(dictation_id, {dictation_id});
|
||||
EXPECT_EQ(dictation_id, controller->current_ime().id);
|
||||
EXPECT_FALSE(controller->IsCurrentImeVisible());
|
||||
EXPECT_EQ(0u, controller->GetVisibleImes().size());
|
||||
|
||||
// Add something else too, but Dictation is active.
|
||||
RefreshImes(dictation_id, {dictation_id, "ime1"});
|
||||
EXPECT_EQ(dictation_id, controller->current_ime().id);
|
||||
EXPECT_FALSE(controller->IsCurrentImeVisible());
|
||||
EXPECT_EQ(1u, controller->GetVisibleImes().size());
|
||||
|
||||
// Inactivate the other IME, leave Dictation in the list.
|
||||
RefreshImes("ime1", {dictation_id, "ime1"});
|
||||
EXPECT_EQ("ime1", controller->current_ime().id);
|
||||
EXPECT_TRUE(controller->IsCurrentImeVisible());
|
||||
EXPECT_EQ(1u, controller->GetVisibleImes().size());
|
||||
}
|
||||
|
||||
TEST_F(ImeControllerImplTest, SetImesManagedByPolicy) {
|
||||
ImeControllerImpl* controller = Shell::Get()->ime_controller();
|
||||
TestImeObserver observer;
|
||||
@ -152,7 +177,7 @@ TEST_F(ImeControllerImplTest, CanSwitchIme) {
|
||||
ImeControllerImpl* controller = Shell::Get()->ime_controller();
|
||||
|
||||
// Can't switch IMEs when none are available.
|
||||
ASSERT_EQ(0u, controller->available_imes().size());
|
||||
ASSERT_EQ(0u, controller->GetVisibleImes().size());
|
||||
EXPECT_FALSE(controller->CanSwitchIme());
|
||||
|
||||
// Can't switch with only 1 IME.
|
||||
@ -208,7 +233,7 @@ TEST_F(ImeControllerImplTest, SwitchImeWithAccelerator) {
|
||||
const ui::Accelerator wide_half_2(ui::VKEY_DBE_DBCSCHAR, ui::EF_NONE);
|
||||
|
||||
// When there are no IMEs available switching by accelerator does not work.
|
||||
ASSERT_EQ(0u, controller->available_imes().size());
|
||||
ASSERT_EQ(0u, controller->GetVisibleImes().size());
|
||||
EXPECT_FALSE(controller->CanSwitchImeWithAccelerator(convert));
|
||||
EXPECT_FALSE(controller->CanSwitchImeWithAccelerator(non_convert));
|
||||
EXPECT_FALSE(controller->CanSwitchImeWithAccelerator(wide_half_1));
|
||||
|
@ -2131,7 +2131,7 @@ void LockContentsView::ShowAuthErrorMessage() {
|
||||
int bold_length = 0;
|
||||
// Display a hint to switch keyboards if there are other active input
|
||||
// methods in clamshell mode.
|
||||
if (ime_controller->available_imes().size() > 1 && !IsTabletMode()) {
|
||||
if (ime_controller->GetVisibleImes().size() > 1 && !IsTabletMode()) {
|
||||
error_text += u" ";
|
||||
bold_start = error_text.length();
|
||||
std::u16string shortcut =
|
||||
|
@ -21,7 +21,7 @@ namespace {
|
||||
bool IsButtonVisible() {
|
||||
DCHECK(Shell::Get());
|
||||
ImeControllerImpl* ime_controller = Shell::Get()->ime_controller();
|
||||
size_t ime_count = ime_controller->available_imes().size();
|
||||
size_t ime_count = ime_controller->GetVisibleImes().size();
|
||||
return !ime_controller->is_menu_active() &&
|
||||
(ime_count > 1 || ime_controller->managed_by_policy());
|
||||
}
|
||||
@ -29,7 +29,7 @@ bool IsButtonVisible() {
|
||||
std::u16string GetLabelString() {
|
||||
DCHECK(Shell::Get());
|
||||
ImeControllerImpl* ime_controller = Shell::Get()->ime_controller();
|
||||
size_t ime_count = ime_controller->available_imes().size();
|
||||
size_t ime_count = ime_controller->GetVisibleImes().size();
|
||||
if (ime_count > 1) {
|
||||
return ime_controller->current_ime().short_name;
|
||||
} else {
|
||||
@ -42,7 +42,7 @@ std::u16string GetLabelString() {
|
||||
std::u16string GetTooltipString() {
|
||||
DCHECK(Shell::Get());
|
||||
ImeControllerImpl* ime_controller = Shell::Get()->ime_controller();
|
||||
size_t ime_count = ime_controller->available_imes().size();
|
||||
size_t ime_count = ime_controller->GetVisibleImes().size();
|
||||
if (ime_count > 1) {
|
||||
return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_IME_TOOLTIP_WITH_NAME,
|
||||
ime_controller->current_ime().name);
|
||||
|
@ -75,7 +75,7 @@ void UnifiedIMEDetailedViewController::OnIMEMenuActivationChanged(
|
||||
void UnifiedIMEDetailedViewController::Update() {
|
||||
ImeControllerImpl* ime_controller = Shell::Get()->ime_controller();
|
||||
view_->Update(ime_controller->current_ime().id,
|
||||
ime_controller->available_imes(),
|
||||
ime_controller->GetVisibleImes(),
|
||||
ime_controller->current_ime_menu_items(),
|
||||
ShouldShowKeyboardToggle(), GetSingleImeBehavior());
|
||||
}
|
||||
|
@ -209,7 +209,7 @@ ImeListView::~ImeListView() = default;
|
||||
void ImeListView::Init(bool show_keyboard_toggle,
|
||||
SingleImeBehavior single_ime_behavior) {
|
||||
ImeControllerImpl* ime_controller = Shell::Get()->ime_controller();
|
||||
Update(ime_controller->current_ime().id, ime_controller->available_imes(),
|
||||
Update(ime_controller->current_ime().id, ime_controller->GetVisibleImes(),
|
||||
ime_controller->current_ime_menu_items(), show_keyboard_toggle,
|
||||
single_ime_behavior);
|
||||
}
|
||||
|
@ -486,7 +486,7 @@ void ImeMenuTray::OnIMERefresh() {
|
||||
UpdateTrayLabel();
|
||||
if (bubble_ && ime_list_view_) {
|
||||
ime_list_view_->Update(
|
||||
ime_controller_->current_ime().id, ime_controller_->available_imes(),
|
||||
ime_controller_->current_ime().id, ime_controller_->GetVisibleImes(),
|
||||
ime_controller_->current_ime_menu_items(), ShouldShowKeyboardToggle(),
|
||||
ImeListView::SHOW_SINGLE_IME);
|
||||
}
|
||||
|
@ -164,6 +164,39 @@ TEST_F(ImeMenuTrayTest, TrayLabelTest) {
|
||||
EXPECT_EQ(u"UK*", GetTrayText());
|
||||
}
|
||||
|
||||
TEST_F(ImeMenuTrayTest, TrayLabelExludesDictation) {
|
||||
Shell::Get()->ime_controller()->ShowImeMenuOnShelf(true);
|
||||
ASSERT_TRUE(IsVisible());
|
||||
|
||||
ImeInfo info1;
|
||||
info1.id = "ime1";
|
||||
info1.name = u"English";
|
||||
info1.short_name = u"US";
|
||||
info1.third_party = false;
|
||||
|
||||
ImeInfo info2;
|
||||
info2.id = "ime2";
|
||||
info2.name = u"English UK";
|
||||
info2.short_name = u"UK";
|
||||
info2.third_party = true;
|
||||
|
||||
ImeInfo dictation;
|
||||
dictation.id = "_ext_ime_egfdjlfmgnehecnclamagfafdccgfndpdictation";
|
||||
dictation.name = u"Dictation";
|
||||
|
||||
// Changes the input method to "ime1".
|
||||
SetCurrentIme("ime1", {info1, dictation, info2});
|
||||
EXPECT_EQ(u"US", GetTrayText());
|
||||
|
||||
// Changes the input method to a third-party IME extension.
|
||||
SetCurrentIme("ime2", {info1, dictation, info2});
|
||||
EXPECT_EQ(u"UK*", GetTrayText());
|
||||
|
||||
// Sets to "dictation", which shouldn't be shown.
|
||||
SetCurrentIme(dictation.id, {info1, dictation, info2});
|
||||
EXPECT_EQ(u"", GetTrayText());
|
||||
}
|
||||
|
||||
// Tests that IME menu tray changes background color when tapped/clicked. And
|
||||
// tests that the background color becomes 'inactive' when disabling the IME
|
||||
// menu feature. Also makes sure that the shelf won't autohide as long as the
|
||||
|
@ -92,7 +92,12 @@ void ImeModeView::Update() {
|
||||
|
||||
ImeControllerImpl* ime_controller = Shell::Get()->ime_controller();
|
||||
|
||||
size_t ime_count = ime_controller->available_imes().size();
|
||||
if (!ime_controller->IsCurrentImeVisible()) {
|
||||
SetVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t ime_count = ime_controller->GetVisibleImes().size();
|
||||
SetVisible(!ime_menu_on_shelf_activated_ &&
|
||||
(ime_count > 1 || ime_controller->managed_by_policy()));
|
||||
|
||||
|
@ -26,7 +26,8 @@ namespace {
|
||||
// 5. Hotwording component extension.
|
||||
// 6. XKB input method component extension.
|
||||
// 7. M17n/T13n/CJK input method component extension.
|
||||
// Once http://crbug.com/292856 is fixed, remove this whitelist.
|
||||
// 8. Accessibility Common extension (used for Dictation)
|
||||
// Once http://crbug.com/292856 is fixed, remove this allowlist.
|
||||
bool IsMediaRequestAllowedForExtension(const extensions::Extension* extension) {
|
||||
return extension->id() == "mppnpdlheglhdfmldimlhpnegondlapf" ||
|
||||
extension->id() == "jokbpnebhdcladagohdnfgjcpejggllo" ||
|
||||
@ -34,7 +35,8 @@ bool IsMediaRequestAllowedForExtension(const extensions::Extension* extension) {
|
||||
extension->id() == "nnckehldicaciogcbchegobnafnjkcne" ||
|
||||
extension->id() == "nbpagnldghgfoolbancepceaanlmhfmd" ||
|
||||
extension->id() == "jkghodnilhceideoidjikpgommlajknk" ||
|
||||
extension->id() == "gjaehgfemfahhmlgpdfknkhdnemmolop";
|
||||
extension->id() == "gjaehgfemfahhmlgpdfknkhdnemmolop" ||
|
||||
extension->id() == "egfdjlfmgnehecnclamagfafdccgfndp";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -71,6 +71,9 @@ js2gtest("accessibility_common_extjs_tests") {
|
||||
"../common/testing/callback_helper.js",
|
||||
"../common/testing/e2e_test_base.js",
|
||||
"../common/testing/mock_accessibility_private.js",
|
||||
"../common/testing/mock_input_ime.js",
|
||||
"../common/testing/mock_input_method_private.js",
|
||||
"../common/testing/mock_language_settings_private.js",
|
||||
]
|
||||
|
||||
# The test base classes generate C++ code with these deps.
|
||||
@ -103,6 +106,8 @@ js_library("accessibility_common") {
|
||||
"$externs_path/accessibility_private.js",
|
||||
"$externs_path/automation.js",
|
||||
"$externs_path/command_line_private.js",
|
||||
"$externs_path/input_method_private.js",
|
||||
"$externs_path/language_settings_private.js",
|
||||
]
|
||||
}
|
||||
|
||||
@ -122,4 +127,9 @@ js_library("magnifier") {
|
||||
|
||||
js_library("dictation") {
|
||||
sources = [ "dictation/dictation.js" ]
|
||||
externs_list = [
|
||||
"$externs_path/accessibility_private.js",
|
||||
"$externs_path/input_method_private.js",
|
||||
"$externs_path/language_settings_private.js",
|
||||
]
|
||||
}
|
||||
|
5
chrome/browser/resources/chromeos/accessibility/accessibility_common/accessibility_common_loader.js
5
chrome/browser/resources/chromeos/accessibility/accessibility_common/accessibility_common_loader.js
@ -60,6 +60,11 @@ export class AccessibilityCommon {
|
||||
{}, this.onDictationUpdated_.bind(this));
|
||||
chrome.accessibilityFeatures.dictation.onChange.addListener(
|
||||
this.onDictationUpdated_.bind(this));
|
||||
|
||||
// AccessibilityCommon is an IME so it shows in the input methods list
|
||||
// when it starts up. Remove from this list, Dictation will add it back
|
||||
// whenever needed.
|
||||
Dictation.removeAsInputMethod();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,6 +2,24 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
/**
|
||||
* Dictation states.
|
||||
* @enum {!number}
|
||||
*/
|
||||
const DictationState = {
|
||||
OFF: 1,
|
||||
STARTING: 2,
|
||||
LISTENING: 3,
|
||||
STOPPING: 4,
|
||||
};
|
||||
|
||||
/**
|
||||
* The IME engine ID for AccessibilityCommon.
|
||||
* @private {string}
|
||||
* @const
|
||||
*/
|
||||
const IME_ENGINE_ID = '_ext_ime_egfdjlfmgnehecnclamagfafdccgfndpdictation';
|
||||
|
||||
/**
|
||||
* Main class for the Chrome OS dictation feature.
|
||||
* Please note: this is being developed behind the flag
|
||||
@ -11,6 +29,24 @@ export class Dictation {
|
||||
constructor() {
|
||||
chrome.accessibilityPrivate.onToggleDictation.addListener(
|
||||
this.onToggleDictation_.bind(this));
|
||||
chrome.input.ime.onFocus.addListener(this.onImeFocus_.bind(this));
|
||||
chrome.input.ime.onBlur.addListener(this.onImeBlur_.bind(this));
|
||||
|
||||
/** @private {number} */
|
||||
this.activeImeContextId_ = -1;
|
||||
|
||||
/**
|
||||
* The engine ID of the previously active IME input method. Used to
|
||||
* restore the previous IME after Dictation is deactivated.
|
||||
* @private {string}
|
||||
*/
|
||||
this.previousImeEngineId_ = '';
|
||||
|
||||
/**
|
||||
* The state of Dictation.
|
||||
* @private {!DictationState}
|
||||
*/
|
||||
this.state_ = DictationState.OFF;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -19,10 +55,86 @@ export class Dictation {
|
||||
* @private
|
||||
*/
|
||||
onToggleDictation_(activated) {
|
||||
if (activated) {
|
||||
// Dictation as a JS extension isn't actually implemented yet, so just
|
||||
// turn off again.
|
||||
chrome.accessibilityPrivate.toggleDictation();
|
||||
if (activated && this.state_ === DictationState.OFF) {
|
||||
this.state_ = DictationState.STARTING;
|
||||
chrome.inputMethodPrivate.getCurrentInputMethod((method) => {
|
||||
if (this.state_ !== DictationState.STARTING) {
|
||||
return;
|
||||
}
|
||||
this.previousImeEngineId_ = method;
|
||||
// Add AccessibilityCommon as an input method and active it.
|
||||
chrome.languageSettingsPrivate.addInputMethod(IME_ENGINE_ID);
|
||||
chrome.inputMethodPrivate.setCurrentInputMethod(IME_ENGINE_ID, () => {
|
||||
if (this.state_ === DictationState.STARTING) {
|
||||
// TODO(crbug.com/1216111): Start speech recognition and
|
||||
// change state to LISTENING after SR starts.
|
||||
} else {
|
||||
// We are no longer starting up - perhaps a stop came
|
||||
// through during the async callbacks. Ensure cleanup
|
||||
// by calling onDictationStopped_.
|
||||
this.onDictationStopped_();
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.onDictationStopped_();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops Dictation in the browser / ash if it wasn't already stopped.
|
||||
* @private
|
||||
*/
|
||||
stopDictation_() {
|
||||
// Stop Dictation if the state isn't already off.
|
||||
if (this.state_ !== DictationState.OFF) {
|
||||
chrome.accessibilityPrivate.toggleDictation();
|
||||
this.state_ = DictationState.STOPPING;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when Dictation has been toggled off. Cleans up IME and local state.
|
||||
* @private
|
||||
*/
|
||||
onDictationStopped_() {
|
||||
if (this.state_ === DictationState.OFF) {
|
||||
return;
|
||||
}
|
||||
this.state_ = DictationState.OFF;
|
||||
// Clean up IME state and reset to the previous IME method.
|
||||
this.activeImeContextId_ = -1;
|
||||
chrome.inputMethodPrivate.setCurrentInputMethod(this.previousImeEngineId_);
|
||||
this.previousImeEngineId_ = '';
|
||||
Dictation.removeAsInputMethod();
|
||||
}
|
||||
|
||||
/**
|
||||
* chrome.input.ime.onFocus callback. Save the active context ID.
|
||||
* @param {chrome.input.ime.InputContext} context Input field context.
|
||||
* @private
|
||||
*/
|
||||
onImeFocus_(context) {
|
||||
this.activeImeContextId_ = context.contextID;
|
||||
}
|
||||
|
||||
/**
|
||||
* chrome.input.ime.onFocus callback. Stops Dictation if the active
|
||||
* context ID lost focus.
|
||||
* @param {number} contextId
|
||||
* @private
|
||||
*/
|
||||
onImeBlur_(contextId) {
|
||||
if (contextId === this.activeImeContextId_) {
|
||||
this.stopDictation_();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes AccessibilityCommon as an input method so it doesn't show up in
|
||||
* the shelf input method picker UI.
|
||||
*/
|
||||
static removeAsInputMethod() {
|
||||
chrome.languageSettingsPrivate.removeInputMethod(IME_ENGINE_ID);
|
||||
}
|
||||
}
|
||||
|
146
chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation_test.js
146
chrome/browser/resources/chromeos/accessibility/accessibility_common/dictation/dictation_test.js
@ -4,11 +4,38 @@
|
||||
|
||||
GEN_INCLUDE(['../../common/testing/e2e_test_base.js']);
|
||||
GEN_INCLUDE(['../../common/testing/mock_accessibility_private.js']);
|
||||
GEN_INCLUDE(['../../common/testing/mock_input_ime.js']);
|
||||
GEN_INCLUDE(['../../common/testing/mock_input_method_private.js']);
|
||||
GEN_INCLUDE(['../../common/testing/mock_language_settings_private.js']);
|
||||
|
||||
/**
|
||||
* Dictation feature using accessibility common extension browser tests.
|
||||
*/
|
||||
DictationE2ETest = class extends E2ETestBase {
|
||||
constructor() {
|
||||
super();
|
||||
this.mockAccessibilityPrivate = MockAccessibilityPrivate;
|
||||
chrome.accessibilityPrivate = this.mockAccessibilityPrivate;
|
||||
|
||||
this.mockInputIme = MockInputIme;
|
||||
chrome.input.ime = this.mockInputIme;
|
||||
|
||||
this.mockInputMethodPrivate = MockInputMethodPrivate;
|
||||
chrome.inputMethodPrivate = this.mockInputMethodPrivate;
|
||||
|
||||
this.mockLanguageSettingsPrivate = MockLanguageSettingsPrivate;
|
||||
chrome.languageSettingsPrivate = this.mockLanguageSettingsPrivate;
|
||||
|
||||
this.dictationEngineId =
|
||||
'_ext_ime_egfdjlfmgnehecnclamagfafdccgfndpdictation';
|
||||
|
||||
// Re-initialize AccessibilityCommon with mock APIs.
|
||||
const reinit = module => {
|
||||
accessibilityCommon = new module.AccessibilityCommon();
|
||||
};
|
||||
import('/accessibility_common/accessibility_common_loader.js').then(reinit);
|
||||
}
|
||||
|
||||
/** @override */
|
||||
testGenCppIncludes() {
|
||||
super.testGenCppIncludes();
|
||||
@ -39,12 +66,125 @@ DictationE2ETest = class extends E2ETestBase {
|
||||
`);
|
||||
super.testGenPreambleCommon('kAccessibilityCommonExtensionId');
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F('DictationE2ETest', 'SanityCheck', function() {
|
||||
this.newCallback(async () => {
|
||||
/**
|
||||
* Waits for Dictation module to be loaded.
|
||||
*/
|
||||
async waitForDictationModule() {
|
||||
await importModule(
|
||||
'Dictation', '/accessibility_common/dictation/dictation.js');
|
||||
assertNotNullNorUndefined(Dictation);
|
||||
// Enable Dictation.
|
||||
await new Promise(resolve => {
|
||||
chrome.accessibilityFeatures.dictation.set({value: true}, resolve);
|
||||
});
|
||||
return new Promise(resolve => {
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a function that runs a callback after the Dictation module has
|
||||
* loaded.
|
||||
* @param {function<>} callback
|
||||
* @returns {function<>}
|
||||
*/
|
||||
runAfterDictationLoad(callback) {
|
||||
return this.newCallback(async () => {
|
||||
await this.waitForDictationModule();
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that Dictation is the active IME.
|
||||
*/
|
||||
checkDictationImeActive() {
|
||||
assertEquals(
|
||||
this.dictationEngineId,
|
||||
this.mockInputMethodPrivate.getCurrentInputMethodForTest());
|
||||
assertTrue(this.mockLanguageSettingsPrivate.hasInputMethod(
|
||||
this.dictationEngineId));
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks that Dictation is not the active IME.
|
||||
* @param {*} opt_activeImeId If we do not expect Dictation IME to be
|
||||
* activated, an optional IME ID that we do expect to be activated.
|
||||
*/
|
||||
checkDictationImeInactive(opt_activeImeId) {
|
||||
assertNotEquals(
|
||||
this.dictationEngineId,
|
||||
this.mockInputMethodPrivate.getCurrentInputMethodForTest());
|
||||
assertFalse(this.mockLanguageSettingsPrivate.hasInputMethod(
|
||||
this.dictationEngineId));
|
||||
if (opt_activeImeId) {
|
||||
assertEquals(
|
||||
opt_activeImeId,
|
||||
this.mockInputMethodPrivate.getCurrentInputMethodForTest());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F('DictationE2ETest', 'SanityCheck', function() {
|
||||
this.runAfterDictationLoad(() => {
|
||||
assertFalse(this.mockAccessibilityPrivate.getDictationActive());
|
||||
})();
|
||||
});
|
||||
|
||||
TEST_F('DictationE2ETest', 'LoadsIMEWhenEnabled', function() {
|
||||
this.runAfterDictationLoad(() => {
|
||||
this.checkDictationImeInactive();
|
||||
|
||||
this.mockAccessibilityPrivate.callOnToggleDictation(true);
|
||||
assertTrue(this.mockAccessibilityPrivate.getDictationActive());
|
||||
this.checkDictationImeActive();
|
||||
|
||||
// Turn off Dictation and make sure it removes as IME
|
||||
this.mockAccessibilityPrivate.callOnToggleDictation(false);
|
||||
assertFalse(this.mockAccessibilityPrivate.getDictationActive());
|
||||
this.checkDictationImeInactive();
|
||||
})();
|
||||
});
|
||||
|
||||
TEST_F('DictationE2ETest', 'TogglesDictationOffWhenIMEBlur', function() {
|
||||
this.runAfterDictationLoad(() => {
|
||||
this.checkDictationImeInactive();
|
||||
|
||||
this.mockAccessibilityPrivate.callOnToggleDictation(true);
|
||||
assertTrue(this.mockAccessibilityPrivate.getDictationActive());
|
||||
this.checkDictationImeActive();
|
||||
|
||||
// Focus an input context.
|
||||
this.mockInputIme.callOnFocus(1);
|
||||
// Blur the input context. Dictation should get toggled off.
|
||||
this.mockInputIme.callOnBlur(1);
|
||||
|
||||
assertFalse(this.mockAccessibilityPrivate.getDictationActive());
|
||||
|
||||
// Now that we've confirmed that Dictation JS tried to toggle Dictation,
|
||||
// via AccessibilityPrivate, we can call the onToggleDictation
|
||||
// callback as AccessibilityManager would do, to allow Dictation JS to clean
|
||||
// up state.
|
||||
this.mockAccessibilityPrivate.callOnToggleDictation(false);
|
||||
|
||||
this.checkDictationImeInactive();
|
||||
})();
|
||||
});
|
||||
|
||||
TEST_F('DictationE2ETest', 'ResetsPreviousIMEAfterDeactivate', function() {
|
||||
this.runAfterDictationLoad(() => {
|
||||
// Set something as the active IME.
|
||||
this.mockInputMethodPrivate.setCurrentInputMethod('keyboard_cat');
|
||||
this.mockLanguageSettingsPrivate.addInputMethod('keyboard_cat');
|
||||
|
||||
// Activate Dictation.
|
||||
this.mockAccessibilityPrivate.callOnToggleDictation(true);
|
||||
assertTrue(this.mockAccessibilityPrivate.getDictationActive());
|
||||
this.checkDictationImeActive();
|
||||
|
||||
// Deactivate Dictation.
|
||||
this.mockAccessibilityPrivate.callOnToggleDictation(false);
|
||||
this.checkDictationImeInactive('keyboard_cat');
|
||||
})();
|
||||
});
|
||||
|
@ -17,10 +17,22 @@
|
||||
"accessibilityFeatures.read",
|
||||
"accessibilityFeatures.modify",
|
||||
"commandLinePrivate",
|
||||
"settingsPrivate"
|
||||
"input",
|
||||
"inputMethodPrivate",
|
||||
"settingsPrivate",
|
||||
"languageSettingsPrivate"
|
||||
],
|
||||
"automation": {
|
||||
"desktop": true
|
||||
},
|
||||
"default_locale": "en"
|
||||
"default_locale": "en",
|
||||
"input_components": [
|
||||
{
|
||||
"name": "Dictation",
|
||||
"type": "ime",
|
||||
"id": "dictation",
|
||||
"description": "Dictation",
|
||||
"language": ["none"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -49,6 +49,12 @@ var MockAccessibilityPrivate = {
|
||||
/** @private {?string} */
|
||||
highlightColor_: null,
|
||||
|
||||
/** @private {function<boolean>} */
|
||||
dictationToggleListener_: null,
|
||||
|
||||
/** @private {boolean} */
|
||||
dictationActivated_: false,
|
||||
|
||||
// Methods from AccessibilityPrivate API. //
|
||||
|
||||
onScrollableBoundsForPointRequested: {
|
||||
@ -62,6 +68,7 @@ var MockAccessibilityPrivate = {
|
||||
|
||||
/**
|
||||
* Removes the listener.
|
||||
* @param {function<number, number>} listener
|
||||
*/
|
||||
removeListener: (listener) => {
|
||||
if (MockAccessibilityPrivate.boundsListener_ === listener) {
|
||||
@ -70,10 +77,8 @@ var MockAccessibilityPrivate = {
|
||||
}
|
||||
},
|
||||
|
||||
onMagnifierBoundsChanged: {
|
||||
addListener: (listener) => {},
|
||||
removeListener: (listener) => {}
|
||||
},
|
||||
onMagnifierBoundsChanged:
|
||||
{addListener: (listener) => {}, removeListener: (listener) => {}},
|
||||
|
||||
onSelectToSpeakPanelAction: {
|
||||
/**
|
||||
@ -86,6 +91,26 @@ var MockAccessibilityPrivate = {
|
||||
},
|
||||
},
|
||||
|
||||
onToggleDictation: {
|
||||
/**
|
||||
* Adds a listener to onToggleDictation.
|
||||
* @param {function<boolean>} listener
|
||||
*/
|
||||
addListener: (listener) => {
|
||||
MockAccessibilityPrivate.dictationToggleListener_ = listener;
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the listener.
|
||||
* @param {function<boolean>} listener
|
||||
*/
|
||||
removeListener: (listener) => {
|
||||
if (MockAccessibilityPrivate.dictationToggleListener_ === listener) {
|
||||
MockAccessibilityPrivate.dictationToggleListener_ = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onSelectToSpeakStateChangeRequested: {
|
||||
/**
|
||||
* Adds a listener to onSelectToSpeakStateChangeRequested.
|
||||
@ -148,6 +173,14 @@ var MockAccessibilityPrivate = {
|
||||
.selectToSpeakPanelState_ = {show, anchor, isPaused, speed};
|
||||
},
|
||||
|
||||
/**
|
||||
* Called in order to toggle Dictation listening.
|
||||
*/
|
||||
toggleDictation: () => {
|
||||
MockAccessibilityPrivate.dictationActivated_ =
|
||||
!MockAccessibilityPrivate.dictationActivated_;
|
||||
},
|
||||
|
||||
// Methods for testing. //
|
||||
|
||||
/**
|
||||
@ -239,4 +272,26 @@ var MockAccessibilityPrivate = {
|
||||
MockAccessibilityPrivate.selectToSpeakStateChangeListener_();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Simulates Dictation activation change from AccessibilityManager, which may
|
||||
* occur when the user or a chrome extension toggles Dictation active state.
|
||||
* @param {boolean} activated
|
||||
*/
|
||||
callOnToggleDictation: (activated) => {
|
||||
MockAccessibilityPrivate.dictationActivated_ = activated;
|
||||
if (MockAccessibilityPrivate.dictationToggleListener_) {
|
||||
MockAccessibilityPrivate.dictationToggleListener_(activated);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the current Dictation active state. This can be flipped when
|
||||
* MockAccessibilityPrivate.toggleDictation is called, and set when
|
||||
* MocakAccessibilityPrivate.callOnToggleDictation is called.
|
||||
* @returns {boolean} The current Dictation active state.
|
||||
*/
|
||||
getDictationActive() {
|
||||
return MockAccessibilityPrivate.dictationActivated_;
|
||||
},
|
||||
};
|
@ -0,0 +1,86 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* contextID: number,
|
||||
* }}
|
||||
*/
|
||||
let InputContext;
|
||||
|
||||
/*
|
||||
* A mock chrome.input.ime API for tests.
|
||||
*/
|
||||
var MockInputIme = {
|
||||
/** @private {function<InputContext>} */
|
||||
onFocusListener_: null,
|
||||
|
||||
/** @private {function<number>} */
|
||||
onBlurListener_: null,
|
||||
|
||||
// Methods from chrome.input.ime API. //
|
||||
|
||||
onFocus: {
|
||||
/**
|
||||
* Adds a listener to onFocus.
|
||||
* @param {function<InputContext>} listener
|
||||
*/
|
||||
addListener: (listener) => {
|
||||
MockInputIme.onFocusListener_ = listener;
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the listener.
|
||||
* @param {function<InputContext>} listener
|
||||
*/
|
||||
removeListener: (listener) => {
|
||||
if (MockInputIme.onFocusListener_ === listener) {
|
||||
MockInputIme.onFocusListener_ = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onBlur: {
|
||||
/**
|
||||
* Adds a listener to onBlur.
|
||||
* @param {function<number>} listener
|
||||
*/
|
||||
addListener: (listener) => {
|
||||
MockInputIme.onBlurListener_ = listener;
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the listener.
|
||||
* @param {function<number>} listener
|
||||
*/
|
||||
removeListener: (listener) => {
|
||||
if (MockInputIme.onBlurListener_ === listener) {
|
||||
MockInputIme.onBlurListener_ = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Methods for testing. //
|
||||
|
||||
/**
|
||||
* Calls listeners for chrome.input.ime.onFocus with a InputContext with the
|
||||
* given contextID.
|
||||
* @param {number} contextID
|
||||
*/
|
||||
callOnFocus(contextID) {
|
||||
if (MockInputIme.onFocusListener_) {
|
||||
MockInputIme.onFocusListener_({contextID});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls listeners for chrome.input.ime.onBlur with the given contextID.
|
||||
* @param {number} contextID
|
||||
*/
|
||||
callOnBlur(contextID) {
|
||||
if (MockInputIme.onBlurListener_) {
|
||||
MockInputIme.onBlurListener_(contextID);
|
||||
}
|
||||
},
|
||||
};
|
42
chrome/browser/resources/chromeos/accessibility/common/testing/mock_input_method_private.js
Normal file
42
chrome/browser/resources/chromeos/accessibility/common/testing/mock_input_method_private.js
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
/*
|
||||
* A mock chrome.inputMethodPrivate API for tests.
|
||||
*/
|
||||
var MockInputMethodPrivate = {
|
||||
/** @private {string} */
|
||||
currentInputMethod_: '',
|
||||
|
||||
// Methods from chrome.inputMethodPrivate API. //
|
||||
|
||||
/**
|
||||
* Gets the current input method.
|
||||
* @param {function<string>} callback
|
||||
*/
|
||||
getCurrentInputMethod(callback) {
|
||||
callback(this.currentInputMethod_);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Sets the current input method.
|
||||
* @param {string} inputMethodId The input method to set.
|
||||
* @param {function<>} callback Callback called on success.
|
||||
*/
|
||||
setCurrentInputMethod(inputMethodId, callback) {
|
||||
MockInputMethodPrivate.currentInputMethod_ = inputMethodId;
|
||||
callback && callback();
|
||||
},
|
||||
|
||||
// Methods for testing. //
|
||||
|
||||
/**
|
||||
* Gets the current input method.
|
||||
* @return {string}
|
||||
*/
|
||||
getCurrentInputMethodForTest() {
|
||||
return MockInputMethodPrivate.currentInputMethod_;
|
||||
},
|
||||
};
|
39
chrome/browser/resources/chromeos/accessibility/common/testing/mock_language_settings_private.js
Normal file
39
chrome/browser/resources/chromeos/accessibility/common/testing/mock_language_settings_private.js
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
/*
|
||||
* A mock chrome.languageSettingsPrivate API for tests.
|
||||
*/
|
||||
var MockLanguageSettingsPrivate = {
|
||||
/** @private {array<string>} */
|
||||
inputMethods: [],
|
||||
|
||||
// Methods from chrome.languageSettingsPrivate API. //
|
||||
|
||||
/**
|
||||
* Adds an input method ID.
|
||||
* @param {string} methodId
|
||||
*/
|
||||
addInputMethod(methodId) {
|
||||
MockLanguageSettingsPrivate.inputMethods.push(methodId);
|
||||
},
|
||||
|
||||
removeInputMethod(methodId) {
|
||||
const index = MockLanguageSettingsPrivate.inputMethods.indexOf(methodId);
|
||||
if (index >= 0) {
|
||||
MockLanguageSettingsPrivate.inputMethods.splice(index, 1);
|
||||
}
|
||||
},
|
||||
|
||||
// Methods for testing. //
|
||||
|
||||
/**
|
||||
* Checks if an input method exists.
|
||||
* @param {stromg} methodId
|
||||
* @return {boolean} True if the method is present.
|
||||
*/
|
||||
hasInputMethod(methodId) {
|
||||
return MockLanguageSettingsPrivate.inputMethods.includes(methodId);
|
||||
},
|
||||
};
|
@ -2,6 +2,11 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// The IME ID for the Accessibility Common extension used by Dictation.
|
||||
/** @type {string} */
|
||||
const ACCESSIBILITY_COMMON_IME_ID =
|
||||
'_ext_ime_egfdjlfmgnehecnclamagfafdccgfndpdictation';
|
||||
|
||||
/**
|
||||
* @fileoverview 'os-settings-add-input-methods-dialog' is a dialog for
|
||||
* adding input methods.
|
||||
@ -92,6 +97,10 @@ Polymer({
|
||||
if (this.languageHelper.isInputMethodEnabled(inputMethod.id)) {
|
||||
return false;
|
||||
}
|
||||
// Don't show the Dictation (Accessibility Common) extension in this list.
|
||||
if (inputMethod.id === ACCESSIBILITY_COMMON_IME_ID) {
|
||||
return false;
|
||||
}
|
||||
// Show input methods whose tags match the query.
|
||||
return inputMethod.tags.some(
|
||||
tag => tag.toLocaleLowerCase().includes(this.lowercaseQueryString_));
|
||||
|
@ -2,6 +2,11 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// The IME ID for the Accessibility Common extension used by Dictation.
|
||||
/** @type {string} */
|
||||
const ACCESSIBILITY_COMMON_IME_ID =
|
||||
'_ext_ime_egfdjlfmgnehecnclamagfafdccgfndpdictation';
|
||||
|
||||
/**
|
||||
* @fileoverview
|
||||
* 'os-settings-languages-section' is the top-level settings section for
|
||||
@ -106,6 +111,9 @@ Polymer({
|
||||
* @private
|
||||
*/
|
||||
getInputMethodDisplayName_(id, languageHelper) {
|
||||
if (id === ACCESSIBILITY_COMMON_IME_ID) {
|
||||
return '';
|
||||
}
|
||||
// LanguageHelper.getInputMethodDisplayName will throw an error if the ID
|
||||
// isn't found, such as when using CrOS on Linux.
|
||||
try {
|
||||
|
@ -47,6 +47,13 @@ const kTranslateLanguageSynonyms = {
|
||||
// one in ui/base/ime/chromeos/extension_ime_util.h.
|
||||
const kArcImeLanguage = '_arc_ime_language_';
|
||||
|
||||
// <if expr="chromeos">
|
||||
// The IME ID for the Accessibility Common extension used by Dictation.
|
||||
/** @type {string} */
|
||||
const ACCESSIBILITY_COMMON_IME_ID =
|
||||
'_ext_ime_egfdjlfmgnehecnclamagfafdccgfndpdictation';
|
||||
// </if>
|
||||
|
||||
let preferredLanguagesPrefName = 'intl.accept_languages';
|
||||
// <if expr="chromeos">
|
||||
preferredLanguagesPrefName = 'settings.language.preferred_languages';
|
||||
@ -1214,11 +1221,13 @@ Polymer({
|
||||
.value.split(','));
|
||||
this.enabledInputMethodSet_ = new Set(enabledInputMethodIds);
|
||||
|
||||
// Return only supported input methods.
|
||||
// Return only supported input methods. Don't include the Dictation
|
||||
// (Accessibility Common) input method.
|
||||
return enabledInputMethodIds
|
||||
.map(id => this.supportedInputMethodMap_.get(id))
|
||||
.filter(function(inputMethod) {
|
||||
return !!inputMethod;
|
||||
return !!inputMethod &&
|
||||
inputMethod.id !== ACCESSIBILITY_COMMON_IME_ID;
|
||||
});
|
||||
},
|
||||
|
||||
|
Reference in New Issue
Block a user