@applicaster/zapp-react-native-utils 12.0.0-alpha.4201443043 → 12.0.0-alpha.4223432180

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.
Files changed (64) hide show
  1. package/analyticsUtils/AnalyticPlayerListener.ts +141 -0
  2. package/analyticsUtils/AnalyticsEvents/index.ts +7 -0
  3. package/analyticsUtils/AnalyticsEvents/sendShareEvent.ts +21 -0
  4. package/analyticsUtils/PlayerAnalyticsManager.ts +90 -0
  5. package/analyticsUtils/events.ts +6 -0
  6. package/analyticsUtils/index.tsx +6 -0
  7. package/analyticsUtils/{analyticsTracker.ts → playerAnalyticsTracker.ts} +19 -10
  8. package/appUtils/accessibilityManager/const.ts +50 -0
  9. package/appUtils/accessibilityManager/hooks.ts +40 -0
  10. package/appUtils/accessibilityManager/index.ts +155 -0
  11. package/appUtils/accessibilityManager/logger.ts +5 -0
  12. package/appUtils/focusManager/index.ts +0 -1
  13. package/appUtils/focusManager/treeDataStructure/Utils/index.js +1 -1
  14. package/appUtils/index.js +2 -0
  15. package/appUtils/keyCodes/keys.web.js +8 -0
  16. package/appUtils/keyCodes/keysByPlatform.js +11 -1
  17. package/appUtils/platform/const.ts +25 -0
  18. package/appUtils/platform/index.ts +3 -0
  19. package/appUtils/platform/platformUtils.ts +218 -0
  20. package/appUtils/playerManager/conts.ts +1 -0
  21. package/appUtils/playerManager/index.ts +20 -32
  22. package/appUtils/playerManager/nativePlayerManager.ts +12 -0
  23. package/appUtils/playerManager/player.ts +9 -3
  24. package/appUtils/playerManager/playerHooks/usePlayerCurrentTime.tsx +57 -0
  25. package/appUtils/playerManager/playerNative.ts +6 -15
  26. package/appUtils/playerManager/usePlayerControllerSetup.tsx +2 -1
  27. package/appUtils/playerManager/usePlayerState.tsx +19 -34
  28. package/arrayUtils/index.ts +11 -0
  29. package/booleanUtils/index.ts +4 -4
  30. package/cellUtils/index.ts +38 -5
  31. package/focusManager/FocusManager.ts +20 -5
  32. package/focusManager/__tests__/FocusManager.test.ts +0 -1
  33. package/functionUtils/index.ts +38 -0
  34. package/index.d.ts +15 -0
  35. package/keyboardState/index.ts +0 -0
  36. package/manifestUtils/defaultManifestConfigurations/player.js +121 -0
  37. package/manifestUtils/keys.js +2 -1
  38. package/navigationUtils/index.ts +49 -1
  39. package/numberUtils/__tests__/toNumber.test.ts +2 -2
  40. package/numberUtils/__tests__/toPositiveNumberWithDefault.test.ts +66 -0
  41. package/numberUtils/__tests__/toPositiveNumberWithDefaultZero.test.ts +66 -0
  42. package/numberUtils/index.ts +20 -0
  43. package/package.json +2 -2
  44. package/playerUtils/configurationGenerator.ts +120 -0
  45. package/playerUtils/imaScriptDom.ts +59 -0
  46. package/pluginUtils/index.ts +9 -0
  47. package/reactHooks/cell-click/__tests__/index.test.js +3 -3
  48. package/reactHooks/cell-click/index.ts +17 -3
  49. package/reactHooks/debugging/__tests__/index.test.js +81 -11
  50. package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +69 -6
  51. package/reactHooks/feed/__tests__/useFeedRefresh.test.tsx +6 -2
  52. package/reactHooks/feed/useBatchLoading.ts +52 -28
  53. package/reactHooks/feed/useFeedRefresh.tsx +9 -7
  54. package/reactHooks/layout/useDimensions/__tests__/useDimensions.test.ts +134 -0
  55. package/reactHooks/layout/useDimensions/useDimensions.ts +2 -0
  56. package/reactHooks/navigation/index.ts +14 -8
  57. package/reactHooks/player/useAutoSeek.tsx +9 -7
  58. package/reactHooks/resolvers/useCellResolver.ts +5 -1
  59. package/reactHooks/useConfiguration.ts +2 -2
  60. package/reactUtils/index.ts +104 -0
  61. package/testUtils/index.tsx +42 -9
  62. package/appUtils/focusManager/FocusableEvents.ts +0 -8
  63. package/appUtils/focusManager/treeDataStructure/Utils/__tests__/Utils_android.test.js +0 -1094
  64. package/appUtils/focusManager/treeDataStructure/Utils/index.android.js +0 -261
@@ -0,0 +1,141 @@
1
+ import { PlayerAnalyticsTracker } from "./playerAnalyticsTracker";
2
+ import { Player } from "../appUtils/playerManager/player";
3
+ import { AD_EVENT, GENERAL_EVENT, PLAYBACK_EVENT, QOS_EVENT } from "./events";
4
+
5
+ type Props = {
6
+ analyticsTracker: any;
7
+ player: any;
8
+ };
9
+
10
+ /* eslint brace-style: 0 */ // --> OFF
11
+ export class AnalyticPlayerListener
12
+ implements QuickBrickPlayer.SharedPlayerCallBacks
13
+ {
14
+ static analyticsListenerID = "analytics-listener";
15
+ private analyticsTracker: PlayerAnalyticsTracker;
16
+ private player: Player;
17
+
18
+ private onVideoLoadCalled; // android only
19
+
20
+ constructor(props: Props) {
21
+ this.analyticsTracker = props.analyticsTracker;
22
+ this.player = props.player;
23
+ }
24
+
25
+ handleAnalyticEvent = (event, data = {}) => {
26
+ if (this.analyticsTracker?.handleAnalyticEvent) {
27
+ this.analyticsTracker?.handleAnalyticEvent?.(event, {
28
+ ...data,
29
+ player_type: this.player.playerPluginId,
30
+ });
31
+ }
32
+ };
33
+
34
+ onVideoEnd = () => {
35
+ this.handleAnalyticEvent(PLAYBACK_EVENT.complete);
36
+ };
37
+
38
+ onError = (err: Error) => {
39
+ this.handleAnalyticEvent(PLAYBACK_EVENT.error, err); // TODO: Check error format
40
+ };
41
+
42
+ onPlayerPause = (event) => {
43
+ this.handleAnalyticEvent(PLAYBACK_EVENT.pause, event);
44
+ };
45
+
46
+ onPlayerResume = (event) => {
47
+ this.handleAnalyticEvent(PLAYBACK_EVENT.play, event);
48
+ };
49
+
50
+ onPlayerSeekComplete = (event) => {
51
+ this.handleAnalyticEvent(PLAYBACK_EVENT.seek_complete, event);
52
+ };
53
+
54
+ onPlayerSeekStart = (event) => {
55
+ this.handleAnalyticEvent(PLAYBACK_EVENT.seek_start, event);
56
+ };
57
+
58
+ onBufferStart = (event) => {
59
+ this.handleAnalyticEvent(PLAYBACK_EVENT.buffer_start, event);
60
+ };
61
+
62
+ onBufferComplete = (event) => {
63
+ this.handleAnalyticEvent(PLAYBACK_EVENT.buffer_complete, event);
64
+ };
65
+
66
+ // Not implemented
67
+ onPlayerStop = (event) => {
68
+ this.handleAnalyticEvent(PLAYBACK_EVENT.stop, event);
69
+ };
70
+
71
+ // TODO: Implement method on native
72
+ onVideoBandwidthUpdate = (event) => {
73
+ this.handleAnalyticEvent(QOS_EVENT.bitrate_changed, event);
74
+ };
75
+
76
+ onAdBegin = (event) => {
77
+ this.handleAnalyticEvent(AD_EVENT.ad_start, event);
78
+ };
79
+
80
+ onAdBreakBegin = (event) => {
81
+ this.handleAnalyticEvent(AD_EVENT.ad_break_start, event);
82
+ };
83
+
84
+ onAdBreakEnd = (event) => {
85
+ this.handleAnalyticEvent(AD_EVENT.ad_break_completed, event);
86
+ };
87
+
88
+ onAdEnd = (event) => {
89
+ this.handleAnalyticEvent(AD_EVENT.ad_complete, event);
90
+ };
91
+
92
+ onAdError = (event) => {
93
+ this.handleAnalyticEvent(AD_EVENT.ad_error, event);
94
+ };
95
+
96
+ onAdRequest = (event) => {
97
+ this.handleAnalyticEvent(AD_EVENT.ad_request, event);
98
+ };
99
+
100
+ onAdClicked = (event) => {
101
+ this.handleAnalyticEvent(AD_EVENT.ad_clicked, event);
102
+ };
103
+
104
+ onAdTapped = (event) => {
105
+ this.handleAnalyticEvent(AD_EVENT.ad_tapped, event);
106
+ };
107
+
108
+ onVideoLoad = () => {
109
+ // if (!this.state.isAd && !this.onVideoLoadCalled) {
110
+ if (!this.player.isAd() && !this.onVideoLoadCalled) {
111
+ this.onVideoLoadCalled = true;
112
+
113
+ this.handleAnalyticEvent(PLAYBACK_EVENT.load_video, {
114
+ player_type: this.player.playerPluginId,
115
+ });
116
+ }
117
+ };
118
+
119
+ onPlayerSessionEnd = () => {
120
+ // TODO: Implement method on native android, ios exists
121
+ this.handleAnalyticEvent(GENERAL_EVENT.session_end);
122
+ };
123
+
124
+ onPlayerSessionStart = () => {
125
+ // TODO: Implement method on native android, ios exists
126
+
127
+ this.handleAnalyticEvent(GENERAL_EVENT.session_start);
128
+ };
129
+
130
+ onPlayerNext = ({ nativeEvent }) => {
131
+ // TODO: Implement method on native
132
+
133
+ this.handleAnalyticEvent(PLAYBACK_EVENT.next, nativeEvent);
134
+ };
135
+
136
+ onPlayerPrevious = ({ nativeEvent }) => {
137
+ // TODO: Implement method on native
138
+
139
+ this.handleAnalyticEvent(PLAYBACK_EVENT.previous, nativeEvent);
140
+ };
141
+ }
@@ -12,6 +12,11 @@ import {
12
12
  sendSearchExecutedEvent,
13
13
  } from "./searchEvents";
14
14
 
15
+ import {
16
+ sendShareInitiatedEvent,
17
+ sendShareSuccessEvent,
18
+ } from "./sendShareEvent";
19
+
15
20
  export {
16
21
  sendScreenEvent,
17
22
  sendOnClickEvent,
@@ -24,4 +29,6 @@ export {
24
29
  sendDownloadsEvent,
25
30
  sendSearchCanceledEvent,
26
31
  sendSearchExecutedEvent,
32
+ sendShareInitiatedEvent,
33
+ sendShareSuccessEvent,
27
34
  };
@@ -0,0 +1,21 @@
1
+ import { log_debug } from "../logger";
2
+ import { postAnalyticEvent } from "../manager";
3
+ import { ANALYTICS_CORE_EVENTS } from "../events";
4
+
5
+ export const sendShareInitiatedEvent = (analyticsProps) => {
6
+ const eventName = ANALYTICS_CORE_EVENTS.SHARE_INITIATED;
7
+ postAnalyticEvent(eventName, analyticsProps);
8
+
9
+ log_debug(`sendShareInitiatedEvent: send event: ${eventName}`, {
10
+ analyticsProps,
11
+ });
12
+ };
13
+
14
+ export const sendShareSuccessEvent = (analyticsProps) => {
15
+ const eventName = ANALYTICS_CORE_EVENTS.SHARE_SUCCESS;
16
+ postAnalyticEvent(eventName, analyticsProps);
17
+
18
+ log_debug(`sendShareSuccessEvent: send event: ${eventName}`, {
19
+ analyticsProps,
20
+ });
21
+ };
@@ -0,0 +1,90 @@
1
+ import {
2
+ PlayerAnalyticsTracker,
3
+ PlayerAnalyticsTrackerI,
4
+ } from "./playerAnalyticsTracker";
5
+ import {
6
+ PlayerLifecycleListener,
7
+ PlayerManager,
8
+ } from "../appUtils/playerManager";
9
+ import { Player } from "../appUtils/playerManager/player";
10
+ import { AnalyticPlayerListener } from "./AnalyticPlayerListener";
11
+ import { PlayerRole } from "../appUtils/playerManager/conts";
12
+
13
+ enum PlayerAnalyticsTrackerType {
14
+ PlayerAnalyticsTracker = "PlayerAnalyticsTracker",
15
+ }
16
+
17
+ class PlayerAnalyticsTrackerFactory {
18
+ createTracker(type: PlayerAnalyticsTrackerType) {
19
+ switch (type) {
20
+ case PlayerAnalyticsTrackerType.PlayerAnalyticsTracker:
21
+ return new PlayerAnalyticsTracker();
22
+ default:
23
+ return new PlayerAnalyticsTracker();
24
+ }
25
+ }
26
+ }
27
+
28
+ export class PlayerAnalyticsManager implements PlayerLifecycleListener {
29
+ static instance: PlayerAnalyticsManager;
30
+ private trackers: Record<string, PlayerAnalyticsTrackerI> = {};
31
+ private trackerFactory: PlayerAnalyticsTrackerFactory;
32
+
33
+ public getTracker(
34
+ id: string,
35
+ type: PlayerAnalyticsTrackerType = PlayerAnalyticsTrackerType.PlayerAnalyticsTracker
36
+ ): PlayerAnalyticsTrackerI {
37
+ if (!this.trackers[id]) {
38
+ this.trackers[id] = this.trackerFactory.createTracker(type);
39
+ }
40
+
41
+ return this.trackers[id];
42
+ }
43
+
44
+ constructor(trackerFactory: PlayerAnalyticsTrackerFactory) {
45
+ this.trackerFactory = trackerFactory;
46
+ this.startListenPlayerManager();
47
+ }
48
+
49
+ static getInstance(
50
+ trackerFactory: PlayerAnalyticsTrackerFactory = new PlayerAnalyticsTrackerFactory()
51
+ ): PlayerAnalyticsManager {
52
+ if (!PlayerAnalyticsManager.instance) {
53
+ PlayerAnalyticsManager.instance = new PlayerAnalyticsManager(
54
+ trackerFactory
55
+ );
56
+ }
57
+
58
+ return PlayerAnalyticsManager.instance;
59
+ }
60
+
61
+ private startListenPlayerManager() {
62
+ PlayerManager.getInstance().addLifecycleListener(this);
63
+ }
64
+
65
+ // PlayerLifecycleListener
66
+
67
+ onRegistered = (player: Player) => {
68
+ if (player.playerRole !== PlayerRole.Primary) {
69
+ return;
70
+ }
71
+
72
+ const tracker = this.getTracker(player.playerId);
73
+ tracker.setPlayer(player);
74
+
75
+ const analyticsListener = new AnalyticPlayerListener({
76
+ analyticsTracker: tracker,
77
+ player,
78
+ });
79
+
80
+ player.addListener({
81
+ id: AnalyticPlayerListener.analyticsListenerID,
82
+ listener: analyticsListener,
83
+ });
84
+ };
85
+
86
+ onUnRegistered = (player: Player) => {
87
+ player.removeListener(AnalyticPlayerListener.analyticsListenerID);
88
+ delete this.trackers[player.playerId];
89
+ };
90
+ }
@@ -28,11 +28,17 @@ export const SEARCH_EVENTS = {
28
28
  SEARCH_CANCELED: "search_canceled",
29
29
  };
30
30
 
31
+ export const SHARE_EVENTS = {
32
+ SHARE_INITIATED: "share_initiated",
33
+ SHARE_SUCCESS: "share_success",
34
+ };
35
+
31
36
  export const ANALYTICS_CORE_EVENTS = {
32
37
  ...LEGACY_EVENTS,
33
38
  ...SCREEN_VIEW_EVENTS,
34
39
  ...TAPPING_EVENTS,
35
40
  ...SEARCH_EVENTS,
41
+ ...SHARE_EVENTS,
36
42
  };
37
43
 
38
44
  export const WEBVIEW_EVENTS = {
@@ -20,6 +20,8 @@ import {
20
20
  sendScreenEvent,
21
21
  sendSearchCanceledEvent,
22
22
  sendSearchExecutedEvent,
23
+ sendShareInitiatedEvent,
24
+ sendShareSuccessEvent,
23
25
  } from "./AnalyticsEvents";
24
26
 
25
27
  import { postAnalyticEvent } from "./manager";
@@ -70,6 +72,8 @@ type AnalyticsFunctions = {
70
72
  }) => void;
71
73
  sendSearchCanceledEvent: (props: AnalyticsDefaultProperties) => void;
72
74
  sendSearchExecutedEvent: (props: AnalyticsDefaultProperties) => void;
75
+ sendShareInitiatedEvent: (props: AnalyticsDefaultProperties) => void;
76
+ sendShareSuccessEvent: (props: AnalyticsDefaultProperties) => void;
73
77
  };
74
78
 
75
79
  type GetAnalyticsFunctions = (
@@ -108,6 +112,8 @@ export function getAnalyticsFunctions({
108
112
  sendDownloadsEvent,
109
113
  sendSearchCanceledEvent,
110
114
  sendSearchExecutedEvent,
115
+ sendShareInitiatedEvent,
116
+ sendShareSuccessEvent,
111
117
  };
112
118
  }
113
119
 
@@ -5,16 +5,25 @@ import { postAnalyticEvent } from "./manager";
5
5
  import { isLive } from "../playerUtils";
6
6
  import { extensionsEvents } from "./AnalyticsEvents/helper";
7
7
  import { AD_EVENT, EVENT_TYPES, PLAYER_DISPLAY_STATES } from "./events";
8
+ import { Player } from "../appUtils/playerManager/player";
8
9
 
9
- export class AnalyticsTracker {
10
+ export interface PlayerAnalyticsTrackerI {
11
+ handleAnalyticEvent(
12
+ event: QuickBrickPlayer.Events,
13
+ eventData?: QuickBrickPlayer.EventData
14
+ ): void;
15
+ setPlayer(player: Player): void;
16
+ }
17
+
18
+ export class PlayerAnalyticsTracker implements PlayerAnalyticsTrackerI {
10
19
  adTime: number | string;
11
20
  mediaTime: number | string;
12
- entry: ZappEntry;
13
21
  eventType: string;
14
22
  playerState: QuickBrickPlayer.NativePlayerState;
23
+ private player: Player;
15
24
 
16
- constructor(entry: any) {
17
- this.entry = entry;
25
+ setPlayer(player: Player) {
26
+ this.player = player;
18
27
  }
19
28
 
20
29
  isAdBreak(event: string): boolean {
@@ -30,6 +39,10 @@ export class AnalyticsTracker {
30
39
  return !!isAdBreak;
31
40
  }
32
41
 
42
+ get entry(): ZappEntry {
43
+ return this.player?.getEntry();
44
+ }
45
+
33
46
  getDuration(
34
47
  event: QuickBrickPlayer.Events,
35
48
  eventData: QuickBrickPlayer.EventData
@@ -215,13 +228,9 @@ export class AnalyticsTracker {
215
228
 
216
229
  handleAnalyticEvent(
217
230
  event: QuickBrickPlayer.Events,
218
- eventData: QuickBrickPlayer.EventData = {},
219
- entry: ZappEntry,
220
- playerState = null
231
+ eventData: QuickBrickPlayer.EventData = {}
221
232
  ) {
222
- this.entry = entry;
223
- this.playerState = playerState;
224
-
233
+ this.playerState = this.player?.getState();
225
234
  postAnalyticEvent(event, this.prepareAnalyticPayload(event, eventData));
226
235
  }
227
236
  }
@@ -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,40 @@
1
+ import { useEffect, useMemo } from "react";
2
+ import { AccessibilityManager } from "./index";
3
+
4
+ /**
5
+ * This hook is used to instantiate the accessibility manager and update localizations used by the class.
6
+ * If the plugin config is not provided or it does not contain any localizations, the accessibility manager will not be updated.
7
+ * We use the plugin configuration because it offers a flattened structure where we can easily access the accessibility labels and hints
8
+ * i.e. pluginConfiguration = {
9
+ * accessibility_close_label: "Close",
10
+ * accessibility_close_hint: "Press here to close",
11
+ * }
12
+ */
13
+ export const useAccessibilityManager = (
14
+ pluginConfiguration: Record<string, any> = {}
15
+ ) => {
16
+ const accessibilityManager = useMemo(() => {
17
+ return AccessibilityManager.getInstance();
18
+ }, []);
19
+
20
+ useEffect(() => {
21
+ if (pluginConfiguration) {
22
+ accessibilityManager.updateLocalizations(pluginConfiguration);
23
+ }
24
+ }, [pluginConfiguration, accessibilityManager]);
25
+
26
+ useEffect(() => {
27
+ const subscription = accessibilityManager.getStateAsObservable().subscribe({
28
+ next: () => {
29
+ // TODO: handle accessibility states
30
+ // screenReaderEnabled: false
31
+ // reduceMotionEnabled: false
32
+ // boldTextEnabled: false
33
+ },
34
+ });
35
+
36
+ return () => subscription.unsubscribe();
37
+ }, [accessibilityManager]);
38
+
39
+ return accessibilityManager;
40
+ };
@@ -0,0 +1,155 @@
1
+ import { BehaviorSubject } from "rxjs";
2
+ import { accessibilityManagerLogger as logger } from "./logger";
3
+ import { TTSManager } from "../platform/platformUtils";
4
+ import { BUTTON_ACCESSIBILITY_KEYS } from "./const";
5
+ import { AccessibilityRole } from "react-native";
6
+
7
+ export class AccessibilityManager {
8
+ private static _instance: AccessibilityManager | null = null;
9
+ private state$ = new BehaviorSubject<AccessibilityState>({
10
+ screenReaderEnabled: false,
11
+ reduceMotionEnabled: false,
12
+ boldTextEnabled: false,
13
+ });
14
+
15
+ private announcements$ = new BehaviorSubject<{
16
+ message: string;
17
+ localizedMessage?: string | undefined;
18
+ timestamp: number;
19
+ } | null>(null);
20
+
21
+ private ttsManager = TTSManager.getInstance();
22
+ private localizations: { [key: string]: string } = {};
23
+
24
+ private constructor() {}
25
+
26
+ public static getInstance(): AccessibilityManager {
27
+ if (!AccessibilityManager._instance) {
28
+ AccessibilityManager._instance = new AccessibilityManager();
29
+ }
30
+
31
+ return AccessibilityManager._instance;
32
+ }
33
+
34
+ /**
35
+ * The method now accepts any object with localizations using a flattened structure
36
+ *
37
+ * i.e. { accessibility_close_label: "Close", accessibility_close_hint: "Press here to close" }
38
+ *
39
+ * No longer accepts:
40
+ *
41
+ * i.e. localizations: [{ en: { accessibility_close_label: "Close", accessibility_close_hint: "Press here to close" } }]
42
+ */
43
+ public updateLocalizations(localizations: { [key: string]: string }) {
44
+ this.localizations = localizations;
45
+ }
46
+
47
+ public getState(): AccessibilityState {
48
+ return this.state$.getValue();
49
+ }
50
+
51
+ public getStateAsObservable() {
52
+ return this.state$.asObservable();
53
+ }
54
+
55
+ /**
56
+ * text you want to be read, if you want to use localized text pass keyOfLocalizedText instead
57
+ * keyOfLocalizedText is the key to the localized text
58
+ */
59
+ public readText({
60
+ text,
61
+ keyOfLocalizedText,
62
+ }: {
63
+ text: string;
64
+ keyOfLocalizedText?: string;
65
+ }) {
66
+ let textToRead = text;
67
+
68
+ if (keyOfLocalizedText) {
69
+ if (!this.localizations) {
70
+ logger.error(
71
+ "Attempting to use localized key without initialized localizations"
72
+ );
73
+
74
+ return;
75
+ }
76
+
77
+ const localizedMessage = this.getLocalizedMessage(keyOfLocalizedText);
78
+
79
+ if (!localizedMessage) {
80
+ logger.warn(`No localization found for key: ${keyOfLocalizedText}`);
81
+
82
+ return;
83
+ }
84
+
85
+ textToRead = localizedMessage;
86
+ }
87
+
88
+ this.ttsManager && this.ttsManager.readText(textToRead);
89
+
90
+ this.announcements$.next({
91
+ message: textToRead,
92
+ localizedMessage: keyOfLocalizedText ? textToRead : undefined,
93
+ timestamp: Date.now(),
94
+ });
95
+ }
96
+
97
+ public getButtonAccessibilityProps(buttonName: string): AccessibilityProps {
98
+ const buttonConfig = BUTTON_ACCESSIBILITY_KEYS[buttonName];
99
+
100
+ if (!buttonConfig) {
101
+ return {
102
+ accessibilityLabel: buttonName,
103
+ accessibilityHint: `Press button to perform action on ${buttonName}`,
104
+ "aria-label": buttonName,
105
+ "aria-description": `Press button to perform action on ${buttonName}`,
106
+ accessibilityRole: "button" as AccessibilityRole,
107
+ "aria-role": "button",
108
+ };
109
+ }
110
+
111
+ const labelKey = buttonConfig.label;
112
+ const hintKey = buttonConfig.hint;
113
+
114
+ const label = this.getLocalizedMessage(labelKey) || buttonName;
115
+
116
+ const hint =
117
+ this.getLocalizedMessage(hintKey) ||
118
+ `Press button to perform action on ${buttonName}`;
119
+
120
+ return {
121
+ accessibilityLabel: label,
122
+ accessibilityHint: hint,
123
+ "aria-label": label,
124
+ "aria-description": hint,
125
+ accessibilityRole: "button" as AccessibilityRole,
126
+ "aria-role": "button",
127
+ };
128
+ }
129
+
130
+ public getInputAccessibilityProps(inputName: string): AccessibilityProps {
131
+ return {
132
+ accessibilityLabel: inputName,
133
+ accessibilityHint: `Enter text into ${inputName}`,
134
+ "aria-label": inputName,
135
+ "aria-description": `Enter text into ${inputName}`,
136
+ accessibilityRole: "textbox" as AccessibilityRole,
137
+ "aria-role": "textbox",
138
+ };
139
+ }
140
+
141
+ public getLocalizedMessage(key: string): string | void {
142
+ if (!key) return;
143
+
144
+ if (!this.localizations) {
145
+ logger.error(
146
+ "Attempting to use localized key without initialized localizations",
147
+ { key }
148
+ );
149
+
150
+ return;
151
+ }
152
+
153
+ return this.localizations[key];
154
+ }
155
+ }
@@ -0,0 +1,5 @@
1
+ import { utilsLogger } from "../../logger";
2
+
3
+ export const accessibilityManagerLogger = utilsLogger.addSubsystem(
4
+ "accessibilityManager"
5
+ );
@@ -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 => {
@@ -206,7 +206,7 @@ export function getTargetInDirection(
206
206
  const overrideTarget = currentNode?.component?.props[overridePropName];
207
207
 
208
208
  if (overrideTarget) {
209
- const target = targetNodes.find((n) => n.id === overrideTarget);
209
+ const target = targetNodes.find((n) => n?.id === overrideTarget);
210
210
 
211
211
  if (target) return target;
212
212
  }
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 **/");
@@ -1,6 +1,7 @@
1
1
  /* global tizen */
2
2
  const isTizen = typeof tizen !== "undefined";
3
3
 
4
+ import { isVizioPlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
4
5
  import * as platformKeys from "./keysByPlatform";
5
6
  import { Platform } from "react-native";
6
7
 
@@ -21,6 +22,13 @@ export const ARROW_KEYS = [
21
22
  KEYS.ArrowRight,
22
23
  ];
23
24
 
25
+ /**
26
+ * Platform.OS currently identifies Vizio as LG_TV, therefore we are using isVizioPlatform to identify platform and add keys.
27
+ */
28
+ if (isVizioPlatform()) {
29
+ Object.assign(KEYS, platformKeys.vizio);
30
+ }
31
+
24
32
  /**
25
33
  * In TizenOS we need to register all of the keys that we are going to use.
26
34
  * You need to register using the name Tizen has for the key.
@@ -102,4 +102,14 @@ const tvos = {
102
102
  Menu: { code: "Menu", eventType: "menu" },
103
103
  };
104
104
 
105
- export { lg_tv, samsung_tv, web, tvos, android_tv };
105
+ const vizio = {
106
+ ...samsung_tv,
107
+ Exit: { code: "Exit", keyCode: 27 },
108
+ PageUp: { code: "PageUp", keyCode: 33 },
109
+ PageDown: { code: "PageDown", keyCode: 34 },
110
+ MediaTrackNext: { code: "MediaTrackNext", keyCode: 418 },
111
+ MediaTrackPrevious: { code: "MediaTrackPrevious", keyCode: 419 },
112
+ PrevCh: { code: "PrevCh", keyCode: 500 },
113
+ };
114
+
115
+ export { lg_tv, samsung_tv, web, tvos, android_tv, vizio };