Commit 28dc1f25 authored by Josiah K's avatar Josiah K Committed by Commit Bot

Add Punctuation Echo Setting to ChromeVox Options Page

The Punctuation Echo Setting has three TTS verbosity levels for speaking punctuation: None, Some, and All.
Before, the user could only change the Punctuation Echo Setting with the Cycle Punctuation Command.
This change allows the user to change the Punctuation Echo Setting from the ChromeVox Options Page.

Bug: 1124445
AX-Relnotes: Add Punctuation Echo Setting to ChromeVox Options Page
Change-Id: I726e43b9dccf23fc12acd83e2430492b042d130d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2388402
Commit-Queue: Josiah Krutz <josiahk@google.com>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Reviewed-by: default avatarAkihiro Ota <akihiroota@chromium.org>
Cr-Commit-Position: refs/heads/master@{#804485}
parent f064d888
......@@ -298,6 +298,39 @@ AbstractTts.FONT_WEIGHT = 'fontWeight';
/** TTS punctuation-echo property. @type {string} */
AbstractTts.PUNCTUATION_ECHO = 'punctuationEcho';
/**
* List of punctuation echoes that the user can cycle through.
* @type {!Array<{name:(string),
* msg:(string),
* regexp:(RegExp),
* clear:(boolean)}>}
*/
AbstractTts.PUNCTUATION_ECHOES = [
// Punctuation echoed for the 'none' option.
{
name: 'none',
msg: 'no_punctuation',
regexp: /[-$#"()*;:<>\n\\\/+='~`@_]/g,
clear: true
},
// Punctuation echoed for the 'some' option.
{
name: 'some',
msg: 'some_punctuation',
regexp: /[$#"*<>\\\/\{\}+=~`%\u2022]/g,
clear: false
},
// Punctuation echoed for the 'all' option.
{
name: 'all',
msg: 'all_punctuation',
regexp: /[-$#"()*;:<>\n\\\/\{\}\[\]+='~`!@_.,?%\u2022]/g,
clear: false
}
];
/** TTS pause property. @type {string} */
AbstractTts.PAUSE = 'pause';
......
......@@ -49,45 +49,6 @@ TtsBackground = class extends ChromeTtsBase {
this.currentPunctuationEcho_ =
parseInt(localStorage[AbstractTts.PUNCTUATION_ECHO] || 1, 10);
/**
* @type {!Array<{name:(string),
* msg:(string),
* regexp:(RegExp),
* clear:(boolean)}>}
* @private
*/
this.punctuationEchoes_ = [
/**
* Punctuation echoed for the 'none' option.
*/
{
name: 'none',
msg: 'no_punctuation',
regexp: /[-$#"()*;:<>\n\\\/+='~`@_]/g,
clear: true
},
/**
* Punctuation echoed for the 'some' option.
*/
{
name: 'some',
msg: 'some_punctuation',
regexp: /[$#"*<>\\\/\{\}+=~`%\u2022]/g,
clear: false
},
/**
* Punctuation echoed for the 'all' option.
*/
{
name: 'all',
msg: 'all_punctuation',
regexp: /[-$#"()*;:<>\n\\\/\{\}\[\]+='~`!@_.,?%\u2022]/g,
clear: false
}
];
/**
* A list of punctuation characters that should always be spliced into
* output even with literal word substitutions. This is important for tts
......@@ -653,17 +614,18 @@ TtsBackground = class extends ChromeTtsBase {
text = super.preprocess(text, properties);
// Perform any remaining processing such as punctuation expansion.
let pE = null;
let punctEcho = null;
if (properties[AbstractTts.PUNCTUATION_ECHO]) {
for (let i = 0; pE = this.punctuationEchoes_[i]; i++) {
if (properties[AbstractTts.PUNCTUATION_ECHO] == pE.name) {
for (let i = 0; punctEcho = AbstractTts.PUNCTUATION_ECHOES[i]; i++) {
if (properties[AbstractTts.PUNCTUATION_ECHO] == punctEcho.name) {
break;
}
}
} else {
pE = this.punctuationEchoes_[this.currentPunctuationEcho_];
punctEcho = AbstractTts.PUNCTUATION_ECHOES[this.currentPunctuationEcho_];
}
text = text.replace(pE.regexp, this.createPunctuationReplace_(pE.clear));
text = text.replace(
punctEcho.regexp, this.createPunctuationReplace_(punctEcho.clear));
// Remove all whitespace from the beginning and end, and collapse all
// inner strings of whitespace to a single space.
......@@ -706,15 +668,26 @@ TtsBackground = class extends ChromeTtsBase {
return previousValue == 0;
}
/**
* Method that updates the punctuation echo level, and also persists setting
* to local storage.
* @param {number} punctuationEcho The index of the desired punctuation echo
* level in AbstractTts.PUNCTUATION_ECHOES.
*/
updatePunctuationEcho(punctuationEcho) {
this.currentPunctuationEcho_ = punctuationEcho;
localStorage[AbstractTts.PUNCTUATION_ECHO] = punctuationEcho;
}
/**
* Method that cycles among the available punctuation echo levels.
* @return {string} The resulting punctuation level message id.
*/
cyclePunctuationEcho() {
this.currentPunctuationEcho_ =
(this.currentPunctuationEcho_ + 1) % this.punctuationEchoes_.length;
localStorage[AbstractTts.PUNCTUATION_ECHO] = this.currentPunctuationEcho_;
return this.punctuationEchoes_[this.currentPunctuationEcho_].msg;
this.updatePunctuationEcho(
(this.currentPunctuationEcho_ + 1) %
AbstractTts.PUNCTUATION_ECHOES.length);
return AbstractTts.PUNCTUATION_ECHOES[this.currentPunctuationEcho_].msg;
}
/**
......
......@@ -173,6 +173,56 @@ SYNC_TEST_F('ChromeVoxTtsBackgroundTest', 'AnnounceCapitalLetters', function() {
assertEquals('A.', preprocess('A.'));
});
SYNC_TEST_F('ChromeVoxTtsBackgroundTest', 'PunctuationMode', function() {
const PUNCTUATION_ECHO_NONE = '0';
const PUNCTUATION_ECHO_SOME = '1';
const PUNCTUATION_ECHO_ALL = '2';
const updatePunctuationEcho = tts.updatePunctuationEcho.bind(tts);
let lastSpokenTextString = '';
tts.speakUsingQueue_ = function(utterance, _) {
lastSpokenTextString = utterance.textString;
};
// No punctuation.
updatePunctuationEcho(PUNCTUATION_ECHO_NONE);
tts.speak(`"That's all, folks!"`);
assertEquals(`That's all, folks!`, lastSpokenTextString);
tts.speak('"$1,234.56 (plus tax) for 78% of your #2 pencils?", they mused');
assertEquals(
'1,234.56 plus tax for 78% of your 2 pencils? , they mused',
lastSpokenTextString);
// Some punctuation.
updatePunctuationEcho(PUNCTUATION_ECHO_SOME);
tts.speak(`"That's all, folks!"`);
assertEquals(`quote That's all, folks! quote`, lastSpokenTextString);
tts.speak('"$1,234.56 (plus tax) for 78% of your #2 pencils?", they mused');
assertEquals(
'quote dollar 1,234.56 (plus tax) for 78 percent of your pound 2 ' +
'pencils? quote , they mused',
lastSpokenTextString);
// All punctuation.
updatePunctuationEcho(PUNCTUATION_ECHO_ALL);
tts.speak(`"That's all, folks!"`);
assertEquals(
`quote That apostrophe' s all comma folks exclamation! quote`,
lastSpokenTextString);
tts.speak('"$1,234.56 (plus tax) for 78% of your #2 pencils?", they mused');
assertEquals(
'quote dollar 1 comma 234 dot 56 open paren plus tax close paren for ' +
'78 percent of your pound 2 pencils question mark? quote comma ' +
'they mused',
lastSpokenTextString);
});
SYNC_TEST_F('ChromeVoxTtsBackgroundTest', 'NumberReadingStyle', function() {
let lastSpokenTextString = '';
tts.speakUsingQueue_ = function(utterance, _) {
......
......@@ -102,6 +102,28 @@
</select>
</div>
<div class="option" id="punctuationEchoOption">
<label id="punctuationEchoLabel" class="i18n"
msgid="options_punctuation_echo_select_label">
Punctuation echo:
</label>
<select id="punctuationEcho" class="pref"
aria-labelledby="punctuationEchoLabel">
<option id="none" class="i18n"
msgid="options_punctuation_echo_none">
None
</option>
<option id="some" class="i18n"
msgid="options_punctuation_echo_some">
Some
</option>
<option id="all" class="i18n"
msgid="options_punctuation_echo_all">
All
</option>
</select>
</div>
<div class="option">
<label id="announceDownloadsLabel" class="i18n"
msgid="options_announce_download">
......
......@@ -9,10 +9,12 @@
goog.provide('OptionsPage');
goog.require('AbstractTts');
goog.require('BluetoothBrailleDisplayUI');
goog.require('ConsoleTts');
goog.require('Msgs');
goog.require('PanelCommand');
goog.require('TtsBackground');
goog.require('BrailleTable');
goog.require('BrailleTranslatorManager');
goog.require('ChromeVox');
......@@ -36,6 +38,8 @@ OptionsPage = class {
OptionsPage.prefs = chrome.extension.getBackgroundPage().prefs;
OptionsPage.consoleTts =
chrome.extension.getBackgroundPage().ConsoleTts.getInstance();
OptionsPage.backgroundTts =
chrome.extension.getBackgroundPage().ChromeVoxState.backgroundTts;
OptionsPage.populateVoicesSelect();
BrailleTable.getAll(function(tables) {
/** @type {!Array<BrailleTable.Table>} */
......@@ -101,6 +105,17 @@ OptionsPage = class {
}
}
if (localStorage[AbstractTts.PUNCTUATION_ECHO]) {
const currentPunctuationEcho =
AbstractTts
.PUNCTUATION_ECHOES[localStorage[AbstractTts.PUNCTUATION_ECHO]];
for (let i = 0, opt; opt = $('punctuationEcho').options[i]; ++i) {
if (opt.id == currentPunctuationEcho.name) {
opt.setAttribute('selected', '');
}
}
}
chrome.commandLinePrivate.hasSwitch(
'disable-experimental-accessibility-chromevox-language-switching',
(enabled) => {
......@@ -445,6 +460,11 @@ OptionsPage = class {
}
} else if (target.className.indexOf('eventstream') != -1) {
OptionsPage.setEventStreamFilter(target.name, target.checked);
} else if (target.id == 'punctuationEcho') {
const selectedPunctuationEcho = target.options[target.selectedIndex].id;
const punctuationEcho = AbstractTts.PUNCTUATION_ECHOES.findIndex(
echo => echo.name === selectedPunctuationEcho);
OptionsPage.backgroundTts.updatePunctuationEcho(punctuationEcho);
} else if (target.classList.contains('pref')) {
if (target.tagName == 'INPUT' && target.type == 'checkbox') {
OptionsPage.prefs.setPref(target.name, target.checked);
......@@ -487,11 +507,16 @@ OptionsPage = class {
OptionsPage.prefs;
/**
* The ChromeVoxConsoleTts object.
* The ConsoleTts object.
* @type {ConsoleTts}
*/
OptionsPage.consoleTts;
/**
* The TtsBackground object.
* @type {TtsBackground}
*/
OptionsPage.backgroundTts;
/**
* Adds event listeners to input boxes to update local storage values and
......
......@@ -84,6 +84,60 @@ TEST_F('ChromeVoxOptionsTest', 'NumberReadingStyleSelect', function() {
});
});
TEST_F('ChromeVoxOptionsTest', 'punctuationEchoSelect', function() {
this.runOnOptionsPage((mockFeedback, evt) => {
const PUNCTUATION_ECHO_NONE = '0';
const PUNCTUATION_ECHO_SOME = '1';
const PUNCTUATION_ECHO_ALL = '2';
const punctuationEchoSelect = evt.target.find({
role: chrome.automation.RoleType.POP_UP_BUTTON,
attributes: {name: 'Punctuation echo:'}
});
assertNotNullNorUndefined(punctuationEchoSelect);
mockFeedback.call(punctuationEchoSelect.focus.bind(punctuationEchoSelect))
.expectSpeech('Punctuation echo:', 'None', 'Collapsed')
.call(punctuationEchoSelect.doDefault.bind(punctuationEchoSelect))
.expectSpeech('Expanded')
// Before selecting the menu option.
.call(() => {
assertEquals(
PUNCTUATION_ECHO_NONE,
localStorage[AbstractTts.PUNCTUATION_ECHO]);
})
.call(press(40 /* ArrowDown */))
.expectSpeech('Some', 'List item', ' 2 of 3 ')
.call(press(13 /* enter */))
// TODO(josiahk): The underlying select behavior here is unexpected
// because we never get a new focus event for the select (moving us
// away from the menu item). We simply repeat the menu item.
.expectSpeech('Some', ' 2 of 3 ')
.call(() => {
assertEquals(
PUNCTUATION_ECHO_SOME,
localStorage[AbstractTts.PUNCTUATION_ECHO]);
})
.call(punctuationEchoSelect.doDefault.bind(punctuationEchoSelect))
.call(press(40 /* ArrowDown */))
.expectSpeech('All', 'List item', ' 3 of 3 ')
.call(press(13 /* enter */))
// TODO(josiahk): The underlying select behavior here is unexpected
// because we never get a new focus event for the select (moving us
// away from the menu item). We simply repeat the menu item.
.expectSpeech('All', ' 3 of 3 ')
.call(() => {
assertEquals(
PUNCTUATION_ECHO_ALL, localStorage[AbstractTts.PUNCTUATION_ECHO]);
})
.replay();
});
});
TEST_F('ChromeVoxOptionsTest', 'SmartStickyMode', function() {
this.runOnOptionsPage((mockFeedback, evt) => {
const smartStickyModeCheckbox = evt.target.find({
......
......@@ -2918,6 +2918,18 @@
<message desc="Describes an option for ChromeVox to read numbers as digits." name="IDS_CHROMEVOX_OPTIONS_NUMBER_READING_STYLE_DIGITS">
Digits
</message>
<message desc="Labels the select for choosing how ChromeVox reads punctuation." name="IDS_CHROMEVOX_OPTIONS_PUNCTUATION_ECHO_SELECT_LABEL">
Punctuation echo:
</message>
<message desc="Describes an option for ChromeVox to echo (speak) no punctuation." name="IDS_CHROMEVOX_OPTIONS_PUNCTUATION_ECHO_NONE">
None
</message>
<message desc="Describes an option for ChromeVox to echo (speak) some punctuation." name="IDS_CHROMEVOX_OPTIONS_PUNCTUATION_ECHO_SOME">
Some
</message>
<message desc="Describes an option for ChromeVox to echo (speak) all punctuation." name="IDS_CHROMEVOX_OPTIONS_PUNCTUATION_ECHO_ALL">
All
</message>
<message desc="Labels the checkbox on the options page that enables displaying Perkins Brailler commands in the ChromeVox menus." name="IDS_CHROMEVOX_OPTIONS_MENU_BRAILLE_COMMANDS">
Show braille commands in the ChromeVox menus
</message>
......
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