@applicaster/zapp-react-native-utils 15.0.0-alpha.1089439460 → 15.0.0-alpha.1308474364
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/hooks.ts +8 -6
- package/appUtils/accessibilityManager/index.ts +28 -1
- package/appUtils/accessibilityManager/utils.ts +36 -5
- package/appUtils/platform/platformUtils.ts +135 -21
- package/appUtils/playerManager/player.ts +0 -4
- package/appUtils/playerManager/usePlayerState.tsx +2 -14
- package/cellUtils/index.ts +40 -1
- package/manifestUtils/defaultManifestConfigurations/player.js +0 -20
- package/manifestUtils/keys.js +21 -0
- package/package.json +2 -2
- package/playerUtils/usePlayerTTS.ts +5 -2
- package/reactHooks/player/TVSeekControlller/TVSeekController.ts +10 -27
|
@@ -17,6 +17,9 @@ export const useAccessibilityManager = (
|
|
|
17
17
|
return AccessibilityManager.getInstance();
|
|
18
18
|
}, []);
|
|
19
19
|
|
|
20
|
+
const [accessibilityManagerState, setAccessibilityManagerState] =
|
|
21
|
+
useState<AccessibilityState>(accessibilityManager.getState());
|
|
22
|
+
|
|
20
23
|
useEffect(() => {
|
|
21
24
|
if (pluginConfiguration) {
|
|
22
25
|
accessibilityManager.updateLocalizations(pluginConfiguration);
|
|
@@ -25,18 +28,17 @@ export const useAccessibilityManager = (
|
|
|
25
28
|
|
|
26
29
|
useEffect(() => {
|
|
27
30
|
const subscription = accessibilityManager.getStateAsObservable().subscribe({
|
|
28
|
-
next: () => {
|
|
29
|
-
|
|
30
|
-
// screenReaderEnabled: false
|
|
31
|
-
// reduceMotionEnabled: false
|
|
32
|
-
// boldTextEnabled: false
|
|
31
|
+
next: (newState) => {
|
|
32
|
+
setAccessibilityManagerState(newState);
|
|
33
33
|
},
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
return () => subscription.unsubscribe();
|
|
37
37
|
}, [accessibilityManager]);
|
|
38
38
|
|
|
39
|
-
return accessibilityManager
|
|
39
|
+
return Object.assign(accessibilityManager, {
|
|
40
|
+
accessibilityManagerState,
|
|
41
|
+
});
|
|
40
42
|
};
|
|
41
43
|
|
|
42
44
|
export const useInitialAnnouncementReady = (
|
|
@@ -36,7 +36,20 @@ export class AccessibilityManager {
|
|
|
36
36
|
false
|
|
37
37
|
);
|
|
38
38
|
|
|
39
|
-
private constructor() {
|
|
39
|
+
private constructor() {
|
|
40
|
+
this.ttsManager
|
|
41
|
+
.getScreenReaderEnabledAsObservable()
|
|
42
|
+
.subscribe((enabled) => {
|
|
43
|
+
const state = this.state$.getValue();
|
|
44
|
+
|
|
45
|
+
if (state.screenReaderEnabled !== enabled) {
|
|
46
|
+
this.state$.next({
|
|
47
|
+
...state,
|
|
48
|
+
screenReaderEnabled: enabled,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
40
53
|
|
|
41
54
|
public static getInstance(): AccessibilityManager {
|
|
42
55
|
if (!AccessibilityManager._instance) {
|
|
@@ -92,8 +105,15 @@ export class AccessibilityManager {
|
|
|
92
105
|
/**
|
|
93
106
|
* Adds a heading to the queue, headings will be read before the next text
|
|
94
107
|
* Each heading will be read once and removed from the queue
|
|
108
|
+
* Does nothing if screen reader is not enabled
|
|
95
109
|
*/
|
|
96
110
|
public addHeading(heading: string) {
|
|
111
|
+
const state = this.state$.getValue();
|
|
112
|
+
|
|
113
|
+
if (!state.screenReaderEnabled) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
97
117
|
if (!this.pendingFocusId) {
|
|
98
118
|
this.pendingFocusId = Date.now().toString();
|
|
99
119
|
}
|
|
@@ -108,6 +128,7 @@ export class AccessibilityManager {
|
|
|
108
128
|
*
|
|
109
129
|
* Implements a delay mechanism to reduce noise during rapid navigation.
|
|
110
130
|
* Only the most recent announcement will be read after the delay period.
|
|
131
|
+
* Does nothing if screen reader is not enabled
|
|
111
132
|
*/
|
|
112
133
|
public readText({
|
|
113
134
|
text,
|
|
@@ -116,6 +137,12 @@ export class AccessibilityManager {
|
|
|
116
137
|
text: string;
|
|
117
138
|
keyOfLocalizedText?: string;
|
|
118
139
|
}) {
|
|
140
|
+
const state = this.state$.getValue();
|
|
141
|
+
|
|
142
|
+
if (!state.screenReaderEnabled) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
119
146
|
let textToRead = text;
|
|
120
147
|
|
|
121
148
|
if (keyOfLocalizedText) {
|
|
@@ -12,13 +12,44 @@ export function calculateReadingTime(
|
|
|
12
12
|
minimumPause: number = 500,
|
|
13
13
|
announcementDelay: number = 700
|
|
14
14
|
): number {
|
|
15
|
-
const
|
|
16
|
-
|
|
15
|
+
const trimmed = text.trim();
|
|
16
|
+
|
|
17
|
+
// Count words (split on whitespace and punctuation, keep alnum boundaries)
|
|
18
|
+
const words = trimmed
|
|
17
19
|
.split(/(?<=\d)(?=[a-zA-Z])|(?<=[a-zA-Z])(?=\d)|[^\w\s]+|\s+/)
|
|
18
20
|
.filter(Boolean).length;
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
// Count spaces - multiple consecutive spaces add extra pause time
|
|
23
|
+
const spaceMatches: string[] = trimmed.match(/\s+/g) || [];
|
|
24
|
+
|
|
25
|
+
const totalSpaces = spaceMatches.reduce(
|
|
26
|
+
(sum: number, match: string) => sum + match.length,
|
|
27
|
+
0
|
|
23
28
|
);
|
|
29
|
+
|
|
30
|
+
const extraSpaces = Math.max(0, totalSpaces - (words - 1)); // words-1 is normal spacing
|
|
31
|
+
|
|
32
|
+
// Heuristic: punctuation increases TTS duration beyond word-based WPM.
|
|
33
|
+
// Commas typically introduce short pauses, sentence terminators longer ones.
|
|
34
|
+
const commaCount = (trimmed.match(/,/g) || []).length;
|
|
35
|
+
const semicolonCount = (trimmed.match(/;/g) || []).length;
|
|
36
|
+
const colonCount = (trimmed.match(/:/g) || []).length;
|
|
37
|
+
const dashCount = (trimmed.match(/\u2013|\u2014|-/g) || []).length; // – — -
|
|
38
|
+
const sentenceEndCount = (trimmed.match(/[.!?](?!\d)/g) || []).length;
|
|
39
|
+
|
|
40
|
+
const commaPauseMs = 220; // short prosody pause for ","
|
|
41
|
+
const midPauseMs = 260; // for ";", ":", dashes
|
|
42
|
+
const sentenceEndPauseMs = 420; // for ".", "!", "?"
|
|
43
|
+
const extraSpacePauseMs = 50; // per extra space beyond normal spacing
|
|
44
|
+
|
|
45
|
+
const punctuationPause =
|
|
46
|
+
commaCount * commaPauseMs +
|
|
47
|
+
(semicolonCount + colonCount + dashCount) * midPauseMs +
|
|
48
|
+
sentenceEndCount * sentenceEndPauseMs +
|
|
49
|
+
extraSpaces * extraSpacePauseMs;
|
|
50
|
+
|
|
51
|
+
const baseByWordsMs = (words / wordsPerMinute) * 60 * 1000;
|
|
52
|
+
const estimatedMs = Math.max(minimumPause, baseByWordsMs + punctuationPause);
|
|
53
|
+
|
|
54
|
+
return estimatedMs + announcementDelay;
|
|
24
55
|
}
|
|
@@ -170,7 +170,9 @@ export const getClosedCaptionState = () => {
|
|
|
170
170
|
*/
|
|
171
171
|
export class TTSManager {
|
|
172
172
|
private ttsState$ = new BehaviorSubject<boolean>(false);
|
|
173
|
+
private screenReaderEnabled$ = new BehaviorSubject<boolean>(false);
|
|
173
174
|
private static ttsManagerInstance: TTSManager;
|
|
175
|
+
private samsungListenerId: number | null = null;
|
|
174
176
|
|
|
175
177
|
private constructor() {
|
|
176
178
|
this.initialize();
|
|
@@ -185,23 +187,114 @@ export class TTSManager {
|
|
|
185
187
|
}
|
|
186
188
|
|
|
187
189
|
async initialize() {
|
|
188
|
-
if (
|
|
190
|
+
if (isVizioPlatform()) {
|
|
191
|
+
document.addEventListener(
|
|
192
|
+
"VIZIO_TTS_ENABLED",
|
|
193
|
+
() => {
|
|
194
|
+
log_debug("Vizio screen reader enabled");
|
|
195
|
+
this.screenReaderEnabled$.next(true);
|
|
196
|
+
},
|
|
197
|
+
false
|
|
198
|
+
);
|
|
189
199
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
200
|
+
document.addEventListener(
|
|
201
|
+
"VIZIO_TTS_DISABLED",
|
|
202
|
+
() => {
|
|
203
|
+
log_debug("Vizio screen reader disabled");
|
|
204
|
+
this.screenReaderEnabled$.next(false);
|
|
205
|
+
},
|
|
206
|
+
false
|
|
207
|
+
);
|
|
208
|
+
}
|
|
197
209
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
210
|
+
if (isLgPlatform() && window.webOS?.service) {
|
|
211
|
+
try {
|
|
212
|
+
// https://webostv.developer.lge.com/develop/references/settings-service
|
|
213
|
+
window.webOS.service.request("luna://com.webos.settingsservice", {
|
|
214
|
+
method: "getSystemSettings",
|
|
215
|
+
parameters: {
|
|
216
|
+
category: "option",
|
|
217
|
+
keys: ["audioGuidance"],
|
|
218
|
+
subscribe: true, // Request a subscription to changes
|
|
219
|
+
},
|
|
220
|
+
onSuccess: (response: any) => {
|
|
221
|
+
const isEnabled = response?.settings?.audioGuidance === "on";
|
|
222
|
+
|
|
223
|
+
log_debug("LG Audio Guidance status changed", {
|
|
224
|
+
isEnabled,
|
|
225
|
+
response,
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
this.screenReaderEnabled$.next(isEnabled);
|
|
229
|
+
},
|
|
230
|
+
onFailure: (error: any) => {
|
|
231
|
+
log_debug("webOS settings subscription failed", { error });
|
|
232
|
+
this.screenReaderEnabled$.next(false);
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
} catch (error) {
|
|
236
|
+
log_debug("webOS settings service request error", { error });
|
|
237
|
+
this.screenReaderEnabled$.next(false);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (isSamsungPlatform() && typeof window.webapis !== "undefined") {
|
|
242
|
+
try {
|
|
243
|
+
if (
|
|
244
|
+
window.webapis?.tvinfo &&
|
|
245
|
+
typeof window.webapis.tvinfo.getMenuValue === "function" &&
|
|
246
|
+
typeof window.webapis.tvinfo.addCaptionChangeListener === "function"
|
|
247
|
+
) {
|
|
248
|
+
// Get initial Voice Guide status
|
|
249
|
+
const initialStatus = window.webapis.tvinfo.getMenuValue(
|
|
250
|
+
window.webapis.tvinfo.TvInfoMenuKey.VOICE_GUIDE_KEY
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
const isEnabled =
|
|
254
|
+
initialStatus === window.webapis.tvinfo.TvInfoMenuValue.ON;
|
|
255
|
+
|
|
256
|
+
log_debug("Samsung Voice Guide initial status", {
|
|
257
|
+
isEnabled,
|
|
258
|
+
initialStatus,
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
this.screenReaderEnabled$.next(isEnabled);
|
|
262
|
+
|
|
263
|
+
// Listen for Voice Guide status changes
|
|
264
|
+
const onChange = () => {
|
|
265
|
+
const currentStatus = window.webapis.tvinfo.getMenuValue(
|
|
266
|
+
window.webapis.tvinfo.TvInfoMenuKey.VOICE_GUIDE_KEY
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
const enabled =
|
|
270
|
+
currentStatus === window.webapis.tvinfo.TvInfoMenuValue.ON;
|
|
271
|
+
|
|
272
|
+
log_debug("Samsung Voice Guide status changed", {
|
|
273
|
+
enabled,
|
|
274
|
+
currentStatus,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
this.screenReaderEnabled$.next(enabled);
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
this.samsungListenerId =
|
|
281
|
+
window.webapis.tvinfo.addCaptionChangeListener(
|
|
282
|
+
window.webapis.tvinfo.TvInfoMenuKey.VOICE_GUIDE_KEY,
|
|
283
|
+
onChange
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
log_debug("Samsung Voice Guide listener registered", {
|
|
287
|
+
listenerId: this.samsungListenerId,
|
|
288
|
+
});
|
|
289
|
+
} else {
|
|
290
|
+
log_debug("Samsung TvInfo API not available");
|
|
291
|
+
this.screenReaderEnabled$.next(false);
|
|
292
|
+
}
|
|
293
|
+
} catch (error) {
|
|
294
|
+
log_debug("Samsung Voice Guide listener error", { error });
|
|
295
|
+
this.screenReaderEnabled$.next(false);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
205
298
|
}
|
|
206
299
|
|
|
207
300
|
getCurrentState(): boolean {
|
|
@@ -212,17 +305,38 @@ export class TTSManager {
|
|
|
212
305
|
return this.ttsState$.asObservable();
|
|
213
306
|
}
|
|
214
307
|
|
|
308
|
+
getScreenReaderEnabledAsObservable() {
|
|
309
|
+
return this.screenReaderEnabled$.asObservable();
|
|
310
|
+
}
|
|
311
|
+
|
|
215
312
|
readText(text: string) {
|
|
216
313
|
this.ttsState$.next(true);
|
|
217
314
|
|
|
218
|
-
if (
|
|
219
|
-
|
|
315
|
+
if (
|
|
316
|
+
isSamsungPlatform() &&
|
|
317
|
+
typeof window.tizen !== "undefined" &&
|
|
318
|
+
window.tizen.speech
|
|
319
|
+
) {
|
|
320
|
+
try {
|
|
321
|
+
const successCallback = () => {
|
|
322
|
+
log_debug("Samsung TTS play started successfully");
|
|
323
|
+
// Estimate reading time and set inactive when done
|
|
324
|
+
this.scheduleTTSComplete(text);
|
|
325
|
+
};
|
|
220
326
|
|
|
221
|
-
|
|
222
|
-
|
|
327
|
+
const errorCallback = (error: any) => {
|
|
328
|
+
log_debug("Samsung TTS error", { error: error?.message || error });
|
|
329
|
+
this.ttsState$.next(false);
|
|
330
|
+
};
|
|
223
331
|
|
|
224
|
-
|
|
225
|
-
|
|
332
|
+
// Clear any previous speech before speaking new text
|
|
333
|
+
window.tizen.speech.stop();
|
|
334
|
+
|
|
335
|
+
window.tizen.speech.speak(text, successCallback, errorCallback);
|
|
336
|
+
} catch (error) {
|
|
337
|
+
log_debug("Samsung TTS speak() error", { error });
|
|
338
|
+
this.ttsState$.next(false);
|
|
339
|
+
}
|
|
226
340
|
}
|
|
227
341
|
|
|
228
342
|
if (isLgPlatform() && window.webOS?.service) {
|
|
@@ -2,7 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import { Player } from "./player";
|
|
3
3
|
import { usePlayer } from "./usePlayer";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
type PlayerState = {
|
|
6
6
|
currentTime: number;
|
|
7
7
|
duration: number;
|
|
8
8
|
seekableDuration: number;
|
|
@@ -10,8 +10,6 @@ export type PlayerState = {
|
|
|
10
10
|
isPaused: boolean;
|
|
11
11
|
isBuffering: boolean;
|
|
12
12
|
isReadyToPlay: boolean;
|
|
13
|
-
trackState?: QuickBrickPlayer.TracksState;
|
|
14
|
-
isAd: boolean;
|
|
15
13
|
};
|
|
16
14
|
|
|
17
15
|
export const usePlayerState = (
|
|
@@ -26,8 +24,6 @@ export const usePlayerState = (
|
|
|
26
24
|
isPaused: null,
|
|
27
25
|
isBuffering: false,
|
|
28
26
|
isReadyToPlay: false,
|
|
29
|
-
trackState: null,
|
|
30
|
-
isAd: false,
|
|
31
27
|
});
|
|
32
28
|
|
|
33
29
|
const player: Player = usePlayer(playerId);
|
|
@@ -41,8 +37,6 @@ export const usePlayerState = (
|
|
|
41
37
|
isPaused: player.isPaused(),
|
|
42
38
|
isBuffering: player.isBuffering(),
|
|
43
39
|
isReadyToPlay: player.isReadyToPlay(),
|
|
44
|
-
trackState: player.getTracksState(),
|
|
45
|
-
isAd: player.isAd(),
|
|
46
40
|
});
|
|
47
41
|
}, [player]);
|
|
48
42
|
|
|
@@ -60,16 +54,10 @@ export const usePlayerState = (
|
|
|
60
54
|
onPlayerPause: onPlayerChangeState,
|
|
61
55
|
onPlayerResume: onPlayerChangeState,
|
|
62
56
|
onPlayerSeekComplete: onPlayerChangeState,
|
|
63
|
-
onTracksChanged: onPlayerChangeState,
|
|
64
|
-
onAdBreakBegin: onPlayerChangeState,
|
|
65
|
-
onAdBreakEnd: onPlayerChangeState,
|
|
66
|
-
onAdBegin: onPlayerChangeState,
|
|
67
|
-
onAdEnd: onPlayerChangeState,
|
|
68
|
-
onAdError: onPlayerChangeState,
|
|
69
57
|
},
|
|
70
58
|
});
|
|
71
59
|
}
|
|
72
|
-
}, [
|
|
60
|
+
}, [player]);
|
|
73
61
|
|
|
74
62
|
return state;
|
|
75
63
|
};
|
package/cellUtils/index.ts
CHANGED
|
@@ -9,7 +9,10 @@ import {
|
|
|
9
9
|
} from "@applicaster/zapp-react-native-utils/stringUtils";
|
|
10
10
|
import { cellUtilsLogger } from "@applicaster/zapp-react-native-utils/cellUtils/logger";
|
|
11
11
|
import { isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
isNotNil,
|
|
14
|
+
isNilOrEmpty,
|
|
15
|
+
} from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
13
16
|
|
|
14
17
|
import { toNumberWithDefault, toNumberWithDefaultZero } from "../numberUtils";
|
|
15
18
|
|
|
@@ -505,3 +508,39 @@ export const getImageContainerMarginStyles = ({ value }: { value: any }) => {
|
|
|
505
508
|
marginRight: value("image_margin_right"),
|
|
506
509
|
};
|
|
507
510
|
};
|
|
511
|
+
|
|
512
|
+
export const withoutNilOrEmpty = (arr: string[]): string[] =>
|
|
513
|
+
arr.filter((item) => !isNilOrEmpty(item));
|
|
514
|
+
|
|
515
|
+
export const isTextLabel = (key: string): boolean =>
|
|
516
|
+
key.includes("text_label_") && key.endsWith("_switch");
|
|
517
|
+
|
|
518
|
+
export const hasTextLabelsEnabled = (
|
|
519
|
+
configuration: Record<string, any>
|
|
520
|
+
): boolean => {
|
|
521
|
+
const textLabelsKeys = Object.keys(configuration).filter(isTextLabel);
|
|
522
|
+
|
|
523
|
+
const picked = textLabelsKeys.reduce(
|
|
524
|
+
(acc, key) => {
|
|
525
|
+
acc[key] = configuration[key];
|
|
526
|
+
|
|
527
|
+
return acc;
|
|
528
|
+
},
|
|
529
|
+
{} as Record<string, any>
|
|
530
|
+
);
|
|
531
|
+
|
|
532
|
+
const pickedValues = Object.values(picked);
|
|
533
|
+
|
|
534
|
+
// Check if any switch value is truthy (true, "true", "1", etc.)
|
|
535
|
+
return pickedValues.some((value) => {
|
|
536
|
+
if (typeof value === "boolean") {
|
|
537
|
+
return value === true;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (typeof value === "string") {
|
|
541
|
+
return value !== "" && value.toLowerCase() !== "false";
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
return Boolean(value);
|
|
545
|
+
});
|
|
546
|
+
};
|
|
@@ -619,26 +619,6 @@ function getPlayerConfiguration({ platform, version }) {
|
|
|
619
619
|
);
|
|
620
620
|
}
|
|
621
621
|
|
|
622
|
-
if (isTV(platform)) {
|
|
623
|
-
general.fields.push(
|
|
624
|
-
{
|
|
625
|
-
key: "liveSeekingEnabled",
|
|
626
|
-
label: "Live Seeking Enabled",
|
|
627
|
-
initial_value: false,
|
|
628
|
-
type: "switch",
|
|
629
|
-
label_tooltip: "Enable Live Seek",
|
|
630
|
-
},
|
|
631
|
-
{
|
|
632
|
-
key: "minimumAllowedSeekableDurationInSeconds",
|
|
633
|
-
label: "Minimum allowed seekable duration in seconds",
|
|
634
|
-
initial_value: 300,
|
|
635
|
-
type: "number_input",
|
|
636
|
-
label_tooltip:
|
|
637
|
-
"If duration less that this value, player will disable 'liveSeekingEnabled' value",
|
|
638
|
-
}
|
|
639
|
-
);
|
|
640
|
-
}
|
|
641
|
-
|
|
642
622
|
if (isMobile(platform)) {
|
|
643
623
|
general.fields.push(
|
|
644
624
|
{
|
package/manifestUtils/keys.js
CHANGED
|
@@ -959,6 +959,27 @@ const TV_CELL_LABEL_FIELDS = [
|
|
|
959
959
|
rules: "conditional",
|
|
960
960
|
conditions: [{ key: "switch", section: "styles", value: true }],
|
|
961
961
|
},
|
|
962
|
+
{
|
|
963
|
+
type: ZAPPIFEST_FIELDS.font_selector.roku,
|
|
964
|
+
suffix: "roku font family",
|
|
965
|
+
tooltip: "",
|
|
966
|
+
rules: "conditional",
|
|
967
|
+
conditions: [{ key: "switch", section: "styles", value: true }],
|
|
968
|
+
},
|
|
969
|
+
{
|
|
970
|
+
type: ZAPPIFEST_FIELDS.number_input,
|
|
971
|
+
suffix: "roku font size",
|
|
972
|
+
tooltip: "",
|
|
973
|
+
rules: "conditional",
|
|
974
|
+
conditions: [{ key: "switch", section: "styles", value: true }],
|
|
975
|
+
},
|
|
976
|
+
{
|
|
977
|
+
type: ZAPPIFEST_FIELDS.number_input,
|
|
978
|
+
suffix: "roku line height",
|
|
979
|
+
tooltip: "",
|
|
980
|
+
rules: "conditional",
|
|
981
|
+
conditions: [{ key: "switch", section: "styles", value: true }],
|
|
982
|
+
},
|
|
962
983
|
{
|
|
963
984
|
type: ZAPPIFEST_FIELDS.select,
|
|
964
985
|
suffix: "text alignment",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-utils",
|
|
3
|
-
"version": "15.0.0-alpha.
|
|
3
|
+
"version": "15.0.0-alpha.1308474364",
|
|
4
4
|
"description": "Applicaster Zapp React Native utilities package",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@applicaster/applicaster-types": "15.0.0-alpha.
|
|
30
|
+
"@applicaster/applicaster-types": "15.0.0-alpha.1308474364",
|
|
31
31
|
"buffer": "^5.2.1",
|
|
32
32
|
"camelize": "^1.0.0",
|
|
33
33
|
"dayjs": "^1.11.10",
|
|
@@ -7,8 +7,11 @@ export const usePlayerTTS = () => {
|
|
|
7
7
|
const player = usePlayer();
|
|
8
8
|
const accessibilityManager = useAccessibilityManager({});
|
|
9
9
|
|
|
10
|
+
const isScreenReaderEnabled =
|
|
11
|
+
accessibilityManager.accessibilityManagerState.screenReaderEnabled;
|
|
12
|
+
|
|
10
13
|
React.useEffect(() => {
|
|
11
|
-
if (player && accessibilityManager) {
|
|
14
|
+
if (player && accessibilityManager && isScreenReaderEnabled) {
|
|
12
15
|
const playerTTS = new PlayerTTS(player, accessibilityManager);
|
|
13
16
|
const unsubscribe = playerTTS.init();
|
|
14
17
|
|
|
@@ -17,5 +20,5 @@ export const usePlayerTTS = () => {
|
|
|
17
20
|
playerTTS.destroy();
|
|
18
21
|
};
|
|
19
22
|
}
|
|
20
|
-
}, [player, accessibilityManager]);
|
|
23
|
+
}, [player, accessibilityManager, isScreenReaderEnabled]);
|
|
21
24
|
};
|
|
@@ -147,34 +147,17 @@ export class TVSeekController
|
|
|
147
147
|
|
|
148
148
|
let targetPos = currentPos;
|
|
149
149
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
);
|
|
158
|
-
} else if (this.currentSeekType === SEEK_TYPE.FORWARD) {
|
|
159
|
-
targetPos = Math.max(0, currentPos - offset);
|
|
160
|
-
} else {
|
|
161
|
-
log_warning(
|
|
162
|
-
`TVSeekController: handleDelayedSeek - invalid seek type: ${this.currentSeekType}`
|
|
163
|
-
);
|
|
164
|
-
}
|
|
150
|
+
if (this.currentSeekType === SEEK_TYPE.FORWARD) {
|
|
151
|
+
targetPos = Math.min(
|
|
152
|
+
currentPos + offset,
|
|
153
|
+
this.playerController.getSeekableDuration()
|
|
154
|
+
);
|
|
155
|
+
} else if (this.currentSeekType === SEEK_TYPE.REWIND) {
|
|
156
|
+
targetPos = Math.max(0, currentPos - offset);
|
|
165
157
|
} else {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
this.playerController.getSeekableDuration()
|
|
170
|
-
);
|
|
171
|
-
} else if (this.currentSeekType === SEEK_TYPE.REWIND) {
|
|
172
|
-
targetPos = Math.max(0, currentPos - offset);
|
|
173
|
-
} else {
|
|
174
|
-
log_warning(
|
|
175
|
-
`TVSeekController: handleDelayedSeek - invalid seek type: ${this.currentSeekType}`
|
|
176
|
-
);
|
|
177
|
-
}
|
|
158
|
+
log_warning(
|
|
159
|
+
`TVSeekController: handleDelayedSeek - invalid seek type: ${this.currentSeekType}`
|
|
160
|
+
);
|
|
178
161
|
}
|
|
179
162
|
|
|
180
163
|
log_debug(
|