Commit f84b652f authored by David Tseng's avatar David Tseng Committed by Commit Bot

Support programmatic edit key commands

- introduce a scoped setter which enables the keyboard during spoken
feedback's lifetime
- remove restriction/message from ChromeVox disclaimer asking user to
enable a11y vk
- re-map some Search-like-function keys
1. Search+Left/Right -> Home/End
2. Search+Shift+Left/Right -> Shift Home/End
3. Search+Ctrl+Left/Right -> Ctrl+Home/End
4. Search+Shift+Ctrl+Left/Right -> Shift+Ctrl+Home/End
- re-map braille commands to edit key commands as follows:
1. dot 3/6 chord -> previous/nextCharacter -> Left/Right
2. dot 2/5 chord -> previous/nextWord -> Ctrl+Left/Right
3. dot 1/4 chord -> previous/nextObject -> Up/Down
4. dot 2-3/5-6 chord -> previous/nextGroup -> Ctrl+Up/Down

(jumpToTop/Bottom remain dots 1-2-3/4-5-6 and trigger ordinary edit key command
mappings).

notes:
we use the vk codepath because it handles compatibility down the stack
for us and potentially triggers other side effects such as auto
complete/IME

Test: with a braille display, ensure a11y vk off. Press incrementally
Ctrl + t; verify new tab shows up.  In Google Docs, press
Search+Left/Right (and all other combos enumerated above). Verify proper
caret/selection afterwards. Turn on sticky mode. Verify Search nav
performs ChromeVox commands rather than send keys.

Do all of the same with a braille display inside of Docs. Verify ability
to edit and navigate directly from display.

Bug: 
Cq-Include-Trybots: master.tryserver.chromium.linux:closure_compilation
Change-Id: I4878c8a930cd643c406294b7f302c9ad2c07ee9f
Reviewed-on: https://chromium-review.googlesource.com/823578
Commit-Queue: David Tseng <dtseng@chromium.org>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#524545}
parent 58bea862
......@@ -149,6 +149,23 @@ class ChromeVoxPanelWidgetObserver : public views::WidgetObserver {
DISALLOW_COPY_AND_ASSIGN(ChromeVoxPanelWidgetObserver);
};
class ScopedKeyboardStateSetter {
public:
ScopedKeyboardStateSetter() : is_enabled_(keyboard::IsKeyboardEnabled()) {
keyboard::SetRequestedKeyboardState(keyboard::KEYBOARD_STATE_ENABLED);
}
~ScopedKeyboardStateSetter() {
if (!is_enabled_)
keyboard::SetRequestedKeyboardState(keyboard::KEYBOARD_STATE_DISABLED);
}
private:
bool is_enabled_;
DISALLOW_COPY_AND_ASSIGN(ScopedKeyboardStateSetter);
};
///////////////////////////////////////////////////////////////////////////////
// AccessibilityStatusEventDetails
......@@ -1459,6 +1476,8 @@ void AccessibilityManager::PostLoadChromeVox() {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
::switches::kEnableAudioFocus);
}
keyboard_state_setter_.reset(new ScopedKeyboardStateSetter());
}
void AccessibilityManager::PostUnloadChromeVox() {
......@@ -1477,6 +1496,8 @@ void AccessibilityManager::PostUnloadChromeVox() {
// In case the user darkened the screen, undarken it now.
scoped_backlights_forced_off_.reset();
keyboard_state_setter_.reset();
}
void AccessibilityManager::PostSwitchChromeVoxProfile() {
......
......@@ -41,6 +41,7 @@ namespace chromeos {
class AccessibilityExtensionLoader;
class AccessibilityHighlightManager;
class ScopedKeyboardStateSetter;
class SelectToSpeakEventHandler;
class SwitchAccessEventHandler;
......@@ -449,6 +450,8 @@ class AccessibilityManager
// Used to force the backlights off to darken the screen.
std::unique_ptr<ash::ScopedBacklightsForcedOff> scoped_backlights_forced_off_;
std::unique_ptr<ScopedKeyboardStateSetter> keyboard_state_setter_;
base::WeakPtrFactory<AccessibilityManager> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(AccessibilityManager);
......
......@@ -421,14 +421,6 @@ cvox.BrailleInputHandler.prototype = {
chrome.virtualKeyboardPrivate.getKeyboardConfig(function(config) {
// Use the virtual keyboard API instead of the IME key event API
// so that these keys work even if the Braille IME is not active.
// The virtual keyboard private api fails silently if the a11y keyboard
// isn't enabled in settings. Let the user know.
if (!config.a11ymode) {
new Output().format('@enable_virtual_keyboard').go();
return;
}
var keyName = /** @type {string} */ (event.standardKeyCode);
var numericCode = cvox.BrailleKeyEvent.keyCodeToLegacyCode(keyName);
if (!goog.isDef(numericCode))
......
......@@ -348,8 +348,13 @@ Background.prototype = {
return false;
var command = BrailleCommandHandler.getCommand(evt.brailleDots);
if (command)
CommandHandler.onCommand(command);
if (command) {
if (!ChromeVoxState.instance.currentRange ||
!ChromeVoxState.instance.currentRange.start.node
.state[StateType.EDITABLE] ||
BrailleCommandHandler.onEditCommand(command))
CommandHandler.onCommand(command);
}
break;
default:
return false;
......
......@@ -449,6 +449,10 @@ TEST_F('BackgroundTest', 'EarconsForControls', function() {
.call(doCmd('nextObject'))
.expectSpeech('Edit text')
.expectEarcon(cvox.Earcon.EDITABLE_TEXT)
// Editable text Search re-mappings are in effect.
.call(doCmd('toggleStickyMode'))
.expectSpeech('Sticky mode enabled')
.call(doCmd('nextObject'))
.expectSpeech('List box')
.expectEarcon(cvox.Earcon.LISTBOX)
......
......@@ -8,7 +8,11 @@
goog.provide('BrailleCommandHandler');
goog.require('BackgroundKeyboardHandler');
goog.scope(function() {
var Mod = constants.ModifierFlag;
/**
* Maps a dot pattern to a command.
* @type {!Object<number, string>}
......@@ -87,6 +91,46 @@ BrailleCommandHandler.getDots = function(command) {
return 0;
};
/**
* Customizes ChromeVox commands when issued from a braille display while within
* editable text.
* @param {string} command
* @return {boolean} True if the command should propagate.
*/
BrailleCommandHandler.onEditCommand = function(command) {
switch (command) {
case 'previousCharacter':
BackgroundKeyboardHandler.sendKeyPress(37, 'ArrowLeft');
break;
case 'nextCharacter':
BackgroundKeyboardHandler.sendKeyPress(39, 'ArrowRight');
break;
case 'previousWord':
BackgroundKeyboardHandler.sendKeyPress(37, 'ArrowLeft', Mod.CONTROL);
break;
case 'nextWord':
BackgroundKeyboardHandler.sendKeyPress(39, 'ArrowRight', Mod.CONTROL);
break;
case 'previousObject':
case 'previousLine':
BackgroundKeyboardHandler.sendKeyPress(38, 'ArrowUp');
break;
case 'nextObject':
case 'nextLine':
BackgroundKeyboardHandler.sendKeyPress(40, 'ArrowDown');
break;
case 'previousGroup':
BackgroundKeyboardHandler.sendKeyPress(38, 'ArrowUp', Mod.CONTROL);
break;
case 'nextGroup':
BackgroundKeyboardHandler.sendKeyPress(40, 'ArrowDown', Mod.CONTROL);
break;
default:
return true;
}
return false;
};
/**
* @private
*/
......
......@@ -18,8 +18,10 @@ goog.scope(function() {
var AutomationEvent = chrome.automation.AutomationEvent;
var AutomationNode = chrome.automation.AutomationNode;
var Dir = constants.Dir;
var Mod = constants.ModifierFlag;
var EventType = chrome.automation.EventType;
var RoleType = chrome.automation.RoleType;
var StateType = chrome.automation.StateType;
/**
* Handles ChromeVox Next commands.
......@@ -238,6 +240,11 @@ CommandHandler.onCommand = function(command) {
return true;
var current = ChromeVoxState.instance.currentRange_;
// Allow edit commands first.
if (!CommandHandler.onEditCommand_(current, command))
return false;
var dir = Dir.FORWARD;
var pred = null;
var predErrorMsg = undefined;
......@@ -903,6 +910,58 @@ CommandHandler.viewGraphicAsBraille_ = function(current) {
}
};
/**
* Provides a partial mapping from ChromeVox key combinations to
* Search-as-a-function key as seen in Chrome OS documentation.
* @param {cursors.Range} current
* @param {string} command
* @return {boolean} True if the command should propagate.
* @private
*/
CommandHandler.onEditCommand_ = function(current, command) {
if (cvox.ChromeVox.isStickyModeOn() || !current || !current.start ||
!current.start.node || !current.start.node.state[StateType.EDITABLE])
return true;
switch (command) {
case 'previousCharacter':
BackgroundKeyboardHandler.sendKeyPress(36, 'Home', Mod.SHIFT);
break;
case 'nextCharacter':
BackgroundKeyboardHandler.sendKeyPress(35, 'End', Mod.SHIFT);
break;
case 'previousWord':
BackgroundKeyboardHandler.sendKeyPress(
36, 'Home', Mod.SHIFT | Mod.CONTROL);
break;
case 'nextWord':
BackgroundKeyboardHandler.sendKeyPress(
35, 'End', Mod.SHIFT | Mod.CONTROL);
break;
case 'previousObject':
BackgroundKeyboardHandler.sendKeyPress(36, 'Home');
break;
case 'nextObject':
BackgroundKeyboardHandler.sendKeyPress(35, 'End');
break;
case 'previousLine':
BackgroundKeyboardHandler.sendKeyPress(33, 'PageUp');
break;
case 'nextLine':
BackgroundKeyboardHandler.sendKeyPress(34, 'PageDown');
break;
case 'jumpToTop':
BackgroundKeyboardHandler.sendKeyPress(36, 'Home', Mod.CONTROL);
break;
case 'jumpToBottom':
BackgroundKeyboardHandler.sendKeyPress(35, 'End', Mod.CONTROL);
break;
default:
return true;
}
return false;
};
/**
* Performs global initialization.
*/
......
......@@ -30,3 +30,14 @@ constants.Dir = {
* @const
*/
constants.OBJECT_MAX_CHARCOUNT = 1500;
/**
* Modifier values used by Chrome.
* See ui/events/event_constants.h
*/
constants.ModifierFlag = {
SHIFT: 2,
CONTROL: 4,
ALT: 8,
SEARCH: 16
};
......@@ -8,7 +8,10 @@
goog.provide('BackgroundKeyboardHandler');
goog.require('ChromeVoxState');
goog.require('Output');
goog.require('cvox.ChromeVoxKbHandler');
goog.require('cvox.ChromeVoxPrefs');
/** @constructor */
BackgroundKeyboardHandler = function() {
......@@ -72,3 +75,24 @@ BackgroundKeyboardHandler.prototype = {
return false;
}
};
/**
* @param {number} keyCode
* @param {string} keyName
* @param {number=} modifiers
* @return {boolean}
*/
BackgroundKeyboardHandler.sendKeyPress = function(keyCode, keyName, modifiers) {
modifiers = modifiers || 0;
var key = {
type: 'keydown',
keyCode: keyCode,
keyName: keyName,
charValue: keyCode,
modifiers: modifiers
};
chrome.virtualKeyboardPrivate.sendKeyEvent(key);
key['type'] = 'keyup';
chrome.virtualKeyboardPrivate.sendKeyEvent(key);
return true;
};
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