Skip to content

Commit 6f02fb0

Browse files
author
Chris Lorenzo
committed
few more tweaks and docs
1 parent 0c757f8 commit 6f02fb0

File tree

2 files changed

+54
-74
lines changed

2 files changed

+54
-74
lines changed

docs/plugins/accessibility/announcer.md

Lines changed: 20 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
1-
# Announcer Mixin
1+
# Announcer Library
22

3-
Extend your app with the `Announcer` class, that when enabled, allows for relevant information to be voiced along the focus path of the application. On Focus Change events, the `Announcer` class traverses the `_focusPath` property collecting strings or promises of strings to announce to the user. The array of information is passed to a speak function which is responsible for converting the text to speech. We include a default speak function
4-
which uses the [speechSynthesis API](https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis), but you can replace this with your own implementation by passing a speak function as the second argument to `Announcer`.
3+
The `Announcer` library allows for relevant information to be voiced along the focus path of the application. By passing the focus path to `Announcer.onFocusChange` the function traverses the `_focusPath` property collecting strings or promises of strings to announce to the user. The array of information is passed to a SpeechEngine which is responsible for converting the text to speech. By default we use the [speechSynthesis API](https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis), but you can replace this by overwriting `Announcer._textToSpeech`.
54

6-
Note: The speechSynth api has some known problems:
7-
https://stackoverflow.com/questions/39391502/js-speechsynthesis-problems-with-the-cancel-method
8-
https://stackoverflow.com/questions/23483990/speechsynthesis-api-onend-callback-not-working
5+
Note: The speechSynth api has some known problems:<br />
6+
https://stackoverflow.com/questions/39391502/js-speechsynthesis-problems-with-the-cancel-method<br />
7+
https://stackoverflow.com/questions/23483990/speechsynthesis-api-onend-callback-not-working<br />
98

109
This class does its best to work around these issues, but speech synth api can randomly fail.
1110

1211
## Usage
1312

14-
Extend your application with `Announcer` before boot:
15-
1613
```js
17-
import { Router, Accessibility } from '@lightningjs/sdk';
18-
const Base = Announcer(Router.App)
19-
export default class App extends Base {
14+
import { Router, {Announcer} = Accessibility } from '@lightningjs/sdk';
15+
export default class App extends Router.App {
16+
...
17+
_focusChange() {
18+
Announcer.onFocusChange(this.application._focusPath);
19+
}
20+
}
2021
```
2122

22-
Set `announcerEnabled` to true in your app and optionally `debug` to true to see console tables of the output as shown below.
23+
Set `Announcer.debug = true` to see console tables of the output as shown below.
2324

2425
| Index | Component | Property | Value |
2526
| ----- | ------------ | ---------- | ------------------------------------------------ |
@@ -38,7 +39,7 @@ Set `announcerEnabled` to true in your app and optionally `debug` to true to see
3839
| 12 | Grid | No Context | |
3940
| 13 | BrowsePage-1 | Context | PAUSE-2 Press LEFT or RIGHT to |
4041

41-
The `Announcer` will travel through the `_focusPath` looking for `component.announce` then `component.title` properties. After collecting those properties it reverses the `_focusPath` looking for `component.announceContext` properties.
42+
The `Announcer` will travel through the `_focusPath` looking for `component.announce` then `component.title` properties. After collecting those properties it reverses the `_focusPath` looking for `component.announceContext`.
4243

4344
### SpeechType
4445

@@ -80,36 +81,20 @@ You may also use `PAUSE-#` to pause speech for # seconds before saying the next
8081
```
8182

8283
## API
83-
84-
### Options
85-
86-
| name | type | readonly | default | description |
87-
| ------------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
88-
| voiceOutDelay | integer | false | 500ms | time in ms to determine when voice out is read. | |
89-
| announcerFocusDebounce | integer | false | 400ms | time in ms to determine when voice out is read. | |
90-
| announcerTimeout | integer | false | 30000ms | time in ms till focus history is reset causing full readout | |
9184
### Properties
9285

9386
| name | type | readonly | description |
9487
| ---------------- | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
95-
| announcerEnabled | boolean | false | flag to turn on or off Announcer |
88+
| enabled | boolean | false | default true - flag to turn on or off Announcer |
9689
| announcerTimeout | number | false | By default the announcer only gets information about what changed between focus paths. The announcerTimeout resets the cache to announce the full focus path when the user has been inactive a certain amount of time. Default value is 5 minutes. |
9790

98-
### Signals
91+
### Methods
9992

10093
| name | args | description |
10194
| ----------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
102-
| $announce | | Performs a manual announce |
95+
| speak | | Performs a manual announce |
10396
| &nbsp; | `announcement` | See _SpeechType_ above |
10497
| &nbsp; | `options` | Object containing one or more boolean flags: <br/><ul><li>append - Appends announcement to the currently announcing series.</li><li>notification - Speaks out notification and then performs $announcerRefresh.</li></ul> |
105-
| $announcerRefresh | depth | Performs an announce using the focusPath - depth can trim known focusPath |
106-
| $announcerCancel | none | Cancels current speaking |
107-
108-
### Events
109-
110-
These stage level events can be listened to using the syntax `this.stage.on('EVENT_NAME', callback);`
111-
112-
| name | args | description |
113-
| ------------------- | ---- | ----------------------------------------------------------------------------------------------------------------------------- |
114-
| announceEnded | - | emitted when all contents of the announce array have finished being read out |
115-
| announceTimoutEnded | - | emitted after the amount of time of announded word count \* 500ms, used to account for the known speechSynth api issues above |
98+
| clearPrevFocus | `depth` | Clears the last known focusPath - depth can trim known focusPath |
99+
| cancel | none | Cancels current speaking |
100+
| setupTimers | `options` | Object containing: <br/><ul><li>focusDebounce - default amount of time to wait after last input before focus change announcing will occur.</li><li>focusChangeTimeout - Amount of time with no input before full announce will occur on next focusChange</li></ul> |

src/Accessibility/Announcer.js

Lines changed: 34 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@
2020
import SpeechEngine from './Speech.js'
2121
import { debounce, getElmName } from './utils.js'
2222

23-
let resetFocusTimer;
24-
let prevFocusPath = [];
25-
let currentlySpeaking;
26-
let voiceOutDisabled = false;
27-
const fiveMinutes = 300000;
23+
let resetFocusPathTimer
24+
let prevFocusPath = []
25+
let currentlySpeaking
26+
let voiceOutDisabled = false
27+
const fiveMinutes = 300000
2828

2929
function onFocusChangeCore(focusPath = []) {
3030
if (!Announcer.enabled) {
@@ -34,8 +34,7 @@ function onFocusChangeCore(focusPath = []) {
3434
const loaded = focusPath.every(elm => !elm.loading)
3535
const focusDiff = focusPath.filter(elm => !prevFocusPath.includes(elm))
3636

37-
resetFocusTimer()
38-
Announcer.cancel()
37+
resetFocusPathTimer()
3938

4039
if (!loaded) {
4140
Announcer.onFocusChange()
@@ -44,7 +43,7 @@ function onFocusChangeCore(focusPath = []) {
4443

4544
prevFocusPath = focusPath.slice(0)
4645

47-
let toAnnounceText = [];
46+
let toAnnounceText = []
4847
let toAnnounce = focusDiff.reduce((acc, elm) => {
4948
if (elm.announce) {
5049
acc.push([getElmName(elm), 'Announce', elm.announce])
@@ -71,8 +70,7 @@ function onFocusChangeCore(focusPath = []) {
7170
}
7271

7372
if (toAnnounceText.length) {
74-
Announcer.cancel()
75-
return currentlySpeaking = Announcer._textToSpeech(
73+
return Announcer.speak(
7674
toAnnounceText.reduce((acc, val) => acc.concat(val), [])
7775
)
7876
}
@@ -84,49 +82,46 @@ const Announcer = {
8482
cancel: function() {
8583
currentlySpeaking && currentlySpeaking.cancel()
8684
},
87-
refresh: function(depth = 0) {
85+
clearPrevFocus: function(depth = 0) {
8886
prevFocusPath = prevFocusPath.slice(0, depth)
89-
resetFocusTimer()
87+
resetFocusPathTimer()
9088
},
9189
speak: function(text, { append = false, notification = false } = {}) {
92-
if (Announcer.enabled) {
93-
Announcer.onFocusChange.flush()
94-
if (append && currentlySpeaking && currentlySpeaking.active) {
95-
currentlySpeaking.append(text)
96-
} else {
97-
Announcer.cancel()
98-
currentlySpeaking = Announcer._textToSpeech(text)
99-
}
100-
101-
if (notification) {
102-
voiceOutDisabled = true
103-
currentlySpeaking.series.finally(() => {
104-
voiceOutDisabled = false
105-
Announcer.refresh()
106-
})
107-
}
90+
if (Announcer.enabled) {
91+
Announcer.onFocusChange.flush()
92+
if (append && currentlySpeaking && currentlySpeaking.active) {
93+
currentlySpeaking.append(text)
94+
} else {
95+
Announcer.cancel()
96+
Announcer._textToSpeech(text)
10897
}
10998

110-
return currentlySpeaking
99+
if (notification) {
100+
voiceOutDisabled = true
101+
currentlySpeaking.series.finally(() => {
102+
voiceOutDisabled = false
103+
Announcer.refresh()
104+
})
105+
}
106+
}
107+
108+
return currentlySpeaking
111109
},
112110
_textToSpeech(toSpeak) {
113111
if (voiceOutDisabled) {
114112
return
115113
}
116114

117-
return SpeechEngine(toSpeak)
115+
return (currentlySpeaking = SpeechEngine(toSpeak))
118116
},
119-
init: function(focusDebounce = 400, focusChangeTimeout = fiveMinutes) {
120-
Announcer.onFocusChange = debounce(
121-
onFocusChangeCore,
122-
focusDebounce
123-
);
117+
setupTimers: function({focusDebounce = 400, focusChangeTimeout = fiveMinutes} = {}) {
118+
Announcer.onFocusChange = debounce(onFocusChangeCore, focusDebounce)
124119

125-
resetFocusTimer = debounce(() => {
120+
resetFocusPathTimer = debounce(() => {
126121
// Reset focus path for full announce
127122
prevFocusPath = []
128123
}, focusChangeTimeout)
129-
}
124+
},
130125
}
131-
Announcer.init();
132-
export default Announcer;
126+
Announcer.setupTimers()
127+
export default Announcer

0 commit comments

Comments
 (0)