@applicaster/zapp-react-native-utils 12.0.0-rc.87 → 12.0.0-rc.89
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/appUtils/accessibilityManager/const.ts +50 -0
- package/appUtils/accessibilityManager/hooks.ts +31 -0
- package/appUtils/accessibilityManager/index.ts +150 -0
- package/appUtils/accessibilityManager/logger.ts +5 -0
- package/appUtils/focusManager/index.ts +0 -1
- package/appUtils/index.js +2 -0
- package/appUtils/platform/platformUtils.ts +59 -1
- package/package.json +2 -2
- package/pluginUtils/index.ts +9 -0
- package/reactHooks/navigation/index.ts +4 -0
- package/reactUtils/index.ts +3 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export const BUTTON_ACCESSIBILITY_KEYS = {
|
|
2
|
+
play: {
|
|
3
|
+
label: "accessibility_play_label",
|
|
4
|
+
hint: "accessibility_play_hint",
|
|
5
|
+
mini: {
|
|
6
|
+
label: "accessibility_play_mini_label",
|
|
7
|
+
hint: "accessibility_play_mini_hint",
|
|
8
|
+
},
|
|
9
|
+
},
|
|
10
|
+
pause: {
|
|
11
|
+
label: "accessibility_pause_label",
|
|
12
|
+
hint: "accessibility_pause_hint",
|
|
13
|
+
mini: {
|
|
14
|
+
label: "accessibility_pause_mini_label",
|
|
15
|
+
hint: "accessibility_pause_mini_hint",
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
rewind: {
|
|
19
|
+
label: "accessibility_rewind_label",
|
|
20
|
+
hint: "accessibility_rewind_hint",
|
|
21
|
+
},
|
|
22
|
+
forward: {
|
|
23
|
+
label: "accessibility_forward_label",
|
|
24
|
+
hint: "accessibility_forward_hint",
|
|
25
|
+
},
|
|
26
|
+
close: {
|
|
27
|
+
label: "accessibility_close_label",
|
|
28
|
+
hint: "accessibility_close_hint",
|
|
29
|
+
mini: {
|
|
30
|
+
label: "accessibility_close_mini_label",
|
|
31
|
+
hint: "accessibility_close_mini_hint",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
maximize: {
|
|
35
|
+
label: "accessibility_maximize_label",
|
|
36
|
+
hint: "accessibility_maximize_hint",
|
|
37
|
+
},
|
|
38
|
+
subtitles: {
|
|
39
|
+
label: "accessibility_subtitle_label",
|
|
40
|
+
hint: "accessibility_subtitle_hint",
|
|
41
|
+
},
|
|
42
|
+
back: {
|
|
43
|
+
label: "accessibility_back_label",
|
|
44
|
+
hint: "accessibility_back_hint",
|
|
45
|
+
},
|
|
46
|
+
fullscreen: {
|
|
47
|
+
label: "accessibility_fullscreen_label",
|
|
48
|
+
hint: "accessibility_fullscreen_hint",
|
|
49
|
+
},
|
|
50
|
+
} as const;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useEffect, useMemo } from "react";
|
|
2
|
+
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
3
|
+
import { AccessibilityManager } from "./index";
|
|
4
|
+
|
|
5
|
+
export const useAccessibilityManager = () => {
|
|
6
|
+
const { remoteConfigurations } = usePickFromState(["remoteConfigurations"]);
|
|
7
|
+
const { localizations } = remoteConfigurations;
|
|
8
|
+
|
|
9
|
+
const accessibilityManager = useMemo(() => {
|
|
10
|
+
return AccessibilityManager.getInstance();
|
|
11
|
+
}, []);
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
accessibilityManager.updateLocalizations(localizations);
|
|
15
|
+
}, [localizations]);
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const subscription = accessibilityManager.getStateAsObservable().subscribe({
|
|
19
|
+
next: () => {
|
|
20
|
+
// TODO: handle accessibility states
|
|
21
|
+
// screenReaderEnabled: false
|
|
22
|
+
// reduceMotionEnabled: false
|
|
23
|
+
// boldTextEnabled: false
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
return () => subscription.unsubscribe();
|
|
28
|
+
}, [accessibilityManager]);
|
|
29
|
+
|
|
30
|
+
return accessibilityManager;
|
|
31
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { BehaviorSubject } from "rxjs";
|
|
2
|
+
import { accessibilityManagerLogger as logger } from "./logger";
|
|
3
|
+
import { TTSManager } from "../platform/platformUtils";
|
|
4
|
+
import { getLocalizations } from "../../localizationUtils";
|
|
5
|
+
import { BUTTON_ACCESSIBILITY_KEYS } from "./const";
|
|
6
|
+
|
|
7
|
+
export interface AccessibilityState {
|
|
8
|
+
screenReaderEnabled: boolean;
|
|
9
|
+
reduceMotionEnabled: boolean;
|
|
10
|
+
boldTextEnabled: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface AccessibilityProps {
|
|
14
|
+
accessibilityLabel?: string;
|
|
15
|
+
accessibilityHint?: string;
|
|
16
|
+
"aria-label"?: string;
|
|
17
|
+
"aria-description"?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class AccessibilityManager {
|
|
21
|
+
private static _instance: AccessibilityManager | null = null;
|
|
22
|
+
private state$ = new BehaviorSubject<AccessibilityState>({
|
|
23
|
+
screenReaderEnabled: false,
|
|
24
|
+
reduceMotionEnabled: false,
|
|
25
|
+
boldTextEnabled: false,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
private announcements$ = new BehaviorSubject<{
|
|
29
|
+
message: string;
|
|
30
|
+
localizedMessage?: string | undefined;
|
|
31
|
+
timestamp: number;
|
|
32
|
+
} | null>(null);
|
|
33
|
+
|
|
34
|
+
private ttsManager = TTSManager.getInstance();
|
|
35
|
+
private localizations: { [key: string]: string };
|
|
36
|
+
|
|
37
|
+
private constructor(localizations?: { [key: string]: string }) {
|
|
38
|
+
if (localizations) {
|
|
39
|
+
this.localizations = getLocalizations({ localizations });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public static getInstance(): AccessibilityManager {
|
|
44
|
+
if (!AccessibilityManager._instance) {
|
|
45
|
+
AccessibilityManager._instance = new AccessibilityManager();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return AccessibilityManager._instance;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public updateLocalizations(localizations: { [key: string]: string }) {
|
|
52
|
+
this.localizations = getLocalizations({ localizations });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public getState(): AccessibilityState {
|
|
56
|
+
return this.state$.getValue();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public getStateAsObservable() {
|
|
60
|
+
return this.state$.asObservable();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* text you want to be read, if you want to use localized text pass keyOfLocalizedText instead
|
|
65
|
+
* keyOfLocalizedText is the key to the localized text
|
|
66
|
+
*/
|
|
67
|
+
public readText({
|
|
68
|
+
text,
|
|
69
|
+
keyOfLocalizedText,
|
|
70
|
+
}: {
|
|
71
|
+
text: string;
|
|
72
|
+
keyOfLocalizedText?: string;
|
|
73
|
+
}) {
|
|
74
|
+
logger.debug("Reading text", { text, keyOfLocalizedText });
|
|
75
|
+
|
|
76
|
+
if (keyOfLocalizedText) {
|
|
77
|
+
if (!this.localizations) {
|
|
78
|
+
logger.error(
|
|
79
|
+
"Attempting to use localized key without initialized localizations"
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const localizedMessage = this.getLocalizedMessage(keyOfLocalizedText);
|
|
86
|
+
|
|
87
|
+
if (!localizedMessage) {
|
|
88
|
+
logger.warn(`No localization found for key: ${keyOfLocalizedText}`);
|
|
89
|
+
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
text = localizedMessage;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (this.ttsManager) {
|
|
97
|
+
this.ttsManager.readText(text);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
this.announcements$.next({
|
|
101
|
+
message: text,
|
|
102
|
+
localizedMessage: keyOfLocalizedText ? text : undefined,
|
|
103
|
+
timestamp: Date.now(),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
public getButtonAccessibilityProps(buttonName: string): AccessibilityProps {
|
|
108
|
+
const buttonConfig = BUTTON_ACCESSIBILITY_KEYS[buttonName];
|
|
109
|
+
|
|
110
|
+
if (!buttonConfig) {
|
|
111
|
+
return {
|
|
112
|
+
accessibilityLabel: buttonName,
|
|
113
|
+
accessibilityHint: `Press button to perform action on ${buttonName}`,
|
|
114
|
+
"aria-label": buttonName,
|
|
115
|
+
"aria-description": `Press button to perform action on ${buttonName}`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const labelKey = buttonConfig.label;
|
|
120
|
+
const hintKey = buttonConfig.hint;
|
|
121
|
+
|
|
122
|
+
const label = this.getLocalizedMessage(labelKey) || buttonName;
|
|
123
|
+
|
|
124
|
+
const hint =
|
|
125
|
+
this.getLocalizedMessage(hintKey) ||
|
|
126
|
+
`Press button to perform action on ${buttonName}`;
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
accessibilityLabel: label,
|
|
130
|
+
accessibilityHint: hint,
|
|
131
|
+
"aria-label": label,
|
|
132
|
+
"aria-description": hint,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
public getLocalizedMessage(key: string): string | void {
|
|
137
|
+
if (!key) return;
|
|
138
|
+
|
|
139
|
+
if (!this.localizations) {
|
|
140
|
+
logger.error(
|
|
141
|
+
"Attempting to use localized key without initialized localizations",
|
|
142
|
+
{ key }
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return this.localizations[key];
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
import { subscriber } from "../../functionUtils";
|
|
14
14
|
import * as FOCUS_EVENTS from "./events";
|
|
15
15
|
import { coreLogger } from "../../logger";
|
|
16
|
-
|
|
17
16
|
const logger = coreLogger.addSubsystem("focusManager");
|
|
18
17
|
|
|
19
18
|
const isFocusEnabled = (focusableItem): boolean => {
|
package/appUtils/index.js
CHANGED
|
@@ -9,6 +9,8 @@ export { KEYS, ARROW_KEYS } from "./keyCodes/keys";
|
|
|
9
9
|
|
|
10
10
|
export { playerManager } from "./playerManager";
|
|
11
11
|
|
|
12
|
+
export { accessibilityManager } from "./accessibilityManager";
|
|
13
|
+
|
|
12
14
|
export function deprecationMessage(fn, replacement) {
|
|
13
15
|
if (__DEV__ && process.env.NODE_ENV !== "test") {
|
|
14
16
|
console.warn("/** DEPRECATION WARNING **/");
|
|
@@ -11,6 +11,9 @@ declare global {
|
|
|
11
11
|
interface Window {
|
|
12
12
|
VIZIO?: {
|
|
13
13
|
exitApplication: () => void;
|
|
14
|
+
Chromevox: {
|
|
15
|
+
play: (text: string) => void;
|
|
16
|
+
};
|
|
14
17
|
setClosedCaptionHandler: (
|
|
15
18
|
callback: (isCCEnabled: boolean) => void
|
|
16
19
|
) => void;
|
|
@@ -63,7 +66,7 @@ export const hasVizioAPIs = () => {
|
|
|
63
66
|
* Manages device based closed captioning state
|
|
64
67
|
* Returns an observable that emits the current state and updates whenever it changes
|
|
65
68
|
*/
|
|
66
|
-
class ClosedCaptioningManager {
|
|
69
|
+
export class ClosedCaptioningManager {
|
|
67
70
|
private ccState$ = new BehaviorSubject<boolean>(false);
|
|
68
71
|
private static ccManagerInstance: ClosedCaptioningManager;
|
|
69
72
|
|
|
@@ -221,3 +224,58 @@ export class TextTrackManager {
|
|
|
221
224
|
return this.controller.getTracksState().textTrackId !== null;
|
|
222
225
|
}
|
|
223
226
|
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Manages device based closed captioning state
|
|
230
|
+
* Returns an observable that emits the current state and updates whenever it changes
|
|
231
|
+
*/
|
|
232
|
+
export class TTSManager {
|
|
233
|
+
private ttsState$ = new BehaviorSubject<boolean>(false);
|
|
234
|
+
private static ttsManagerInstance: TTSManager;
|
|
235
|
+
|
|
236
|
+
private constructor() {
|
|
237
|
+
this.initialize();
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
static getInstance(): TTSManager {
|
|
241
|
+
if (!TTSManager.ttsManagerInstance) {
|
|
242
|
+
TTSManager.ttsManagerInstance = new TTSManager();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return TTSManager.ttsManagerInstance;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async initialize() {
|
|
249
|
+
if (!isVizioPlatform()) return;
|
|
250
|
+
|
|
251
|
+
document.addEventListener(
|
|
252
|
+
"VIZIO_TTS_ENABLED",
|
|
253
|
+
() => {
|
|
254
|
+
this.ttsState$.next(true);
|
|
255
|
+
},
|
|
256
|
+
false
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
document.addEventListener(
|
|
260
|
+
"VIZIO_TTS_DISABLED",
|
|
261
|
+
() => {
|
|
262
|
+
this.ttsState$.next(false);
|
|
263
|
+
},
|
|
264
|
+
false
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
getCurrentState(): boolean {
|
|
269
|
+
return this.ttsState$.getValue();
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
getStateAsObservable() {
|
|
273
|
+
return this.ttsState$.asObservable();
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
readText(text: string) {
|
|
277
|
+
if (!window.VIZIO?.Chromevox) return;
|
|
278
|
+
|
|
279
|
+
window.VIZIO.Chromevox.play(text);
|
|
280
|
+
}
|
|
281
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-utils",
|
|
3
|
-
"version": "12.0.0-rc.
|
|
3
|
+
"version": "12.0.0-rc.89",
|
|
4
4
|
"description": "Applicaster Zapp React Native utilities package",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@applicaster/applicaster-types": "12.0.0-rc.
|
|
31
|
+
"@applicaster/applicaster-types": "12.0.0-rc.89",
|
|
32
32
|
"buffer": "^5.2.1",
|
|
33
33
|
"camelize": "^1.0.0",
|
|
34
34
|
"create-react-context": "^0.2.3",
|
package/pluginUtils/index.ts
CHANGED
|
@@ -245,3 +245,12 @@ export function generateManifest({ manifestConfig, version }) {
|
|
|
245
245
|
|
|
246
246
|
return manifestConfig({ platform, version });
|
|
247
247
|
}
|
|
248
|
+
|
|
249
|
+
const getPluginType = R.pathOr("unknown_plugin_type", [
|
|
250
|
+
"state",
|
|
251
|
+
"screen",
|
|
252
|
+
"plugin_type",
|
|
253
|
+
]);
|
|
254
|
+
|
|
255
|
+
export const isPlayerPlugin = (routeState): boolean =>
|
|
256
|
+
getPluginType(routeState) === "player";
|
package/reactUtils/index.ts
CHANGED
|
@@ -112,6 +112,9 @@ export const isAndroidTVPlatform = () => getPlatform() === "android_tv";
|
|
|
112
112
|
declare global {
|
|
113
113
|
interface Window {
|
|
114
114
|
VIZIO?: {
|
|
115
|
+
Chromevox: {
|
|
116
|
+
play: (text: string) => void;
|
|
117
|
+
};
|
|
115
118
|
exitApplication: () => void;
|
|
116
119
|
setClosedCaptionHandler: (
|
|
117
120
|
callback: (isCCEnabled: boolean) => void
|