@applicaster/zapp-react-native-utils 12.0.0-alpha.4201443043 → 12.0.0-alpha.4271512420
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/analyticsUtils/AnalyticsEvents/index.ts +7 -0
- package/analyticsUtils/AnalyticsEvents/sendShareEvent.ts +21 -0
- package/analyticsUtils/events.ts +6 -0
- package/analyticsUtils/index.tsx +6 -0
- 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/focusManager/treeDataStructure/Utils/index.js +1 -1
- package/appUtils/index.js +2 -0
- package/appUtils/keyCodes/keys.web.js +8 -0
- package/appUtils/keyCodes/keysByPlatform.js +11 -1
- package/appUtils/platform/const.ts +25 -0
- package/appUtils/platform/index.ts +3 -0
- package/appUtils/platform/platformUtils.ts +185 -0
- package/appUtils/playerManager/conts.ts +1 -0
- package/appUtils/playerManager/index.ts +11 -5
- package/appUtils/playerManager/nativePlayerManager.ts +12 -0
- package/appUtils/playerManager/player.ts +9 -3
- package/appUtils/playerManager/playerNative.ts +5 -14
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +1 -0
- package/booleanUtils/index.ts +4 -4
- package/cellUtils/index.ts +5 -5
- package/keyboardState/index.ts +0 -0
- package/navigationUtils/index.ts +48 -0
- package/package.json +2 -2
- package/pluginUtils/index.ts +9 -0
- package/reactHooks/feed/__tests__/useFeedRefresh.test.tsx +6 -2
- package/reactHooks/feed/useFeedRefresh.tsx +9 -7
- package/reactHooks/navigation/index.ts +13 -7
- package/reactUtils/index.ts +105 -0
- package/appUtils/focusManager/FocusableEvents.ts +0 -8
- package/appUtils/focusManager/treeDataStructure/Utils/__tests__/Utils_android.test.js +0 -1094
- package/appUtils/focusManager/treeDataStructure/Utils/index.android.js +0 -261
|
@@ -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
|
+
};
|
package/analyticsUtils/events.ts
CHANGED
|
@@ -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 = {
|
package/analyticsUtils/index.tsx
CHANGED
|
@@ -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
|
|
|
@@ -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 => {
|
|
@@ -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
|
|
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
|
-
|
|
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 };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platforms found in userAgent string, please note that simulators
|
|
3
|
+
* might not have the same agents that the devices have
|
|
4
|
+
*/
|
|
5
|
+
export const PLATFORMS = {
|
|
6
|
+
webos: "Web0S",
|
|
7
|
+
tizen: "Tizen",
|
|
8
|
+
samsung: "SMART-TV",
|
|
9
|
+
mac: "Mac",
|
|
10
|
+
win: "Win",
|
|
11
|
+
linux: "Linux",
|
|
12
|
+
iphone: "iPhone",
|
|
13
|
+
android: "Android",
|
|
14
|
+
mobile: "Mobile",
|
|
15
|
+
vizio: "VIZIO",
|
|
16
|
+
smartcast: "SmartCast",
|
|
17
|
+
conjure: "Conjure",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Keys used to store the platform / device state in the session storage
|
|
22
|
+
*/
|
|
23
|
+
export const PLATFORM_KEYS = {
|
|
24
|
+
deviceClosedCaptioningEnabled: "device_closed_captioning_enabled",
|
|
25
|
+
};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { BehaviorSubject } from "rxjs";
|
|
2
|
+
|
|
3
|
+
import { sessionStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/SessionStorage";
|
|
4
|
+
|
|
5
|
+
import { PLATFORM_KEYS, PLATFORMS } from "./const";
|
|
6
|
+
import { createLogger, utilsLogger } from "../../logger";
|
|
7
|
+
|
|
8
|
+
declare global {
|
|
9
|
+
interface Window {
|
|
10
|
+
VIZIO?: {
|
|
11
|
+
exitApplication: () => void;
|
|
12
|
+
Chromevox: {
|
|
13
|
+
play: (text: string) => void;
|
|
14
|
+
};
|
|
15
|
+
setClosedCaptionHandler: (
|
|
16
|
+
callback: (isCCEnabled: boolean) => void
|
|
17
|
+
) => void;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const { log_debug } = createLogger({
|
|
23
|
+
category: "General",
|
|
24
|
+
subsystem: "PlatformUtils",
|
|
25
|
+
parent: utilsLogger,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export const getUserAgent = () =>
|
|
29
|
+
window?.navigator?.userAgent || global?.navigator?.userAgent;
|
|
30
|
+
|
|
31
|
+
/** VIZIO UTILS */
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Checks if we are on the Vizio platform by using the userAgent string
|
|
35
|
+
*
|
|
36
|
+
* Use this method when you need to identify the platform on runtime
|
|
37
|
+
* before the VIZIO Companion Library has loaded
|
|
38
|
+
*/
|
|
39
|
+
export const isVizioPlatform = () => {
|
|
40
|
+
const userAgent = getUserAgent();
|
|
41
|
+
|
|
42
|
+
const isVizioAgent =
|
|
43
|
+
userAgent.includes(PLATFORMS.vizio) ||
|
|
44
|
+
userAgent.includes(PLATFORMS.smartcast) ||
|
|
45
|
+
userAgent.includes(PLATFORMS.conjure);
|
|
46
|
+
|
|
47
|
+
return isVizioAgent;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Checks if we are on the Vizio platform by checking for the global VIZIO object
|
|
52
|
+
* and the VIZIO Companion Library. This method does not use the userAgent string,
|
|
53
|
+
* because it mainly checks whether we have access to apis, which may not be available yet
|
|
54
|
+
*/
|
|
55
|
+
export const hasVizioAPIs = () => {
|
|
56
|
+
const hasAPIs =
|
|
57
|
+
typeof window.VIZIO !== "undefined" &&
|
|
58
|
+
window?.applicaster?.vizioLibraryDidLoad;
|
|
59
|
+
|
|
60
|
+
return hasAPIs;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Manages device based closed captioning state
|
|
65
|
+
* Returns an observable that emits the current state and updates whenever it changes
|
|
66
|
+
*/
|
|
67
|
+
export class ClosedCaptioningManager {
|
|
68
|
+
private ccState$ = new BehaviorSubject<boolean>(false);
|
|
69
|
+
private static ccManagerInstance: ClosedCaptioningManager;
|
|
70
|
+
|
|
71
|
+
private constructor() {
|
|
72
|
+
this.initialize();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
static getInstance(): ClosedCaptioningManager {
|
|
76
|
+
if (!ClosedCaptioningManager.ccManagerInstance) {
|
|
77
|
+
ClosedCaptioningManager.ccManagerInstance = new ClosedCaptioningManager();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return ClosedCaptioningManager.ccManagerInstance;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async initialize() {
|
|
84
|
+
const existingState = await sessionStorage.getItem(
|
|
85
|
+
PLATFORM_KEYS.deviceClosedCaptioningEnabled
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
log_debug("Initializing closed captioning manager", { existingState });
|
|
89
|
+
|
|
90
|
+
if (existingState !== null) {
|
|
91
|
+
this.ccState$.next(existingState === "true" || existingState === true);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (hasVizioAPIs()) {
|
|
95
|
+
window.VIZIO!.setClosedCaptionHandler((isCCEnabled) => {
|
|
96
|
+
log_debug("CC state changed", { isCCEnabled });
|
|
97
|
+
|
|
98
|
+
sessionStorage.setItem(
|
|
99
|
+
PLATFORM_KEYS.deviceClosedCaptioningEnabled,
|
|
100
|
+
isCCEnabled
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
this.ccState$.next(isCCEnabled);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
getCurrentState(): boolean {
|
|
109
|
+
return this.ccState$.getValue();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
getStateAsObservable() {
|
|
113
|
+
return this.ccState$.asObservable();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Subscribe to closed captioning state changes
|
|
119
|
+
* @returns Observable<boolean>
|
|
120
|
+
* @example
|
|
121
|
+
* const subscription = getClosedCaptionState().subscribe(
|
|
122
|
+
* isEnabled => setClosedCaptionState(isEnabled)
|
|
123
|
+
* );
|
|
124
|
+
* subscription.unsubscribe(), []);
|
|
125
|
+
*/
|
|
126
|
+
export const getClosedCaptionState = () => {
|
|
127
|
+
const ccManager = ClosedCaptioningManager.getInstance();
|
|
128
|
+
|
|
129
|
+
return ccManager.getStateAsObservable();
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Manages device based closed captioning state
|
|
134
|
+
* Returns an observable that emits the current state and updates whenever it changes
|
|
135
|
+
*/
|
|
136
|
+
export class TTSManager {
|
|
137
|
+
private ttsState$ = new BehaviorSubject<boolean>(false);
|
|
138
|
+
private static ttsManagerInstance: TTSManager;
|
|
139
|
+
|
|
140
|
+
private constructor() {
|
|
141
|
+
this.initialize();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
static getInstance(): TTSManager {
|
|
145
|
+
if (!TTSManager.ttsManagerInstance) {
|
|
146
|
+
TTSManager.ttsManagerInstance = new TTSManager();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return TTSManager.ttsManagerInstance;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async initialize() {
|
|
153
|
+
if (!isVizioPlatform()) return;
|
|
154
|
+
|
|
155
|
+
document.addEventListener(
|
|
156
|
+
"VIZIO_TTS_ENABLED",
|
|
157
|
+
() => {
|
|
158
|
+
this.ttsState$.next(true);
|
|
159
|
+
},
|
|
160
|
+
false
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
document.addEventListener(
|
|
164
|
+
"VIZIO_TTS_DISABLED",
|
|
165
|
+
() => {
|
|
166
|
+
this.ttsState$.next(false);
|
|
167
|
+
},
|
|
168
|
+
false
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
getCurrentState(): boolean {
|
|
173
|
+
return this.ttsState$.getValue();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
getStateAsObservable() {
|
|
177
|
+
return this.ttsState$.asObservable();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
readText(text: string) {
|
|
181
|
+
if (!window.VIZIO?.Chromevox) return;
|
|
182
|
+
|
|
183
|
+
window.VIZIO.Chromevox.play(text);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from "@applicaster/quick-brick-core/App/NetworkStatusProvider/utils";
|
|
12
12
|
import { setUserLanguagePreference } from "./userLanguagePreference";
|
|
13
13
|
import { PlayerRole } from "./conts";
|
|
14
|
+
import { RNPlayerManager } from "./nativePlayerManager";
|
|
14
15
|
|
|
15
16
|
const { log_error, log_debug, log_warning } = createLogger({
|
|
16
17
|
category: "General",
|
|
@@ -164,6 +165,10 @@ export class PlayerManager {
|
|
|
164
165
|
this.addPlayer(id, playerController);
|
|
165
166
|
|
|
166
167
|
if (playerController.playerRole === PlayerRole.Primary) {
|
|
168
|
+
if (playerController.playerPluginId !== "QuickBrickPlayerPlugin") {
|
|
169
|
+
RNPlayerManager?.["QuickBrickPlayerPlugin"]?.stopBackgroundPlayback?.();
|
|
170
|
+
}
|
|
171
|
+
|
|
167
172
|
playerController.addListener({
|
|
168
173
|
id: this.getPlayerListenerId(id),
|
|
169
174
|
listener: {
|
|
@@ -361,9 +366,9 @@ export class PlayerManager {
|
|
|
361
366
|
// TODO: Remove
|
|
362
367
|
if (this.castingReceiver) {
|
|
363
368
|
this.castingReceiver.rewind(deltaTime);
|
|
369
|
+
} else {
|
|
370
|
+
this.getActivePlayer()?.rewind(deltaTime);
|
|
364
371
|
}
|
|
365
|
-
|
|
366
|
-
this.getActivePlayer()?.rewind(deltaTime);
|
|
367
372
|
}
|
|
368
373
|
|
|
369
374
|
public forward(deltaTime = null) {
|
|
@@ -371,9 +376,9 @@ export class PlayerManager {
|
|
|
371
376
|
|
|
372
377
|
if (this.castingReceiver) {
|
|
373
378
|
this.castingReceiver.forward(deltaTime);
|
|
379
|
+
} else {
|
|
380
|
+
this.getActivePlayer()?.forward(deltaTime);
|
|
374
381
|
}
|
|
375
|
-
|
|
376
|
-
this.getActivePlayer()?.forward(deltaTime);
|
|
377
382
|
}
|
|
378
383
|
|
|
379
384
|
public seeking(time) {
|
|
@@ -436,7 +441,7 @@ export class PlayerManager {
|
|
|
436
441
|
}
|
|
437
442
|
|
|
438
443
|
// Call method on player just in case
|
|
439
|
-
return this.getActivePlayer()?.
|
|
444
|
+
return this.getActivePlayer()?.selectTrack(track);
|
|
440
445
|
}
|
|
441
446
|
|
|
442
447
|
public isRegistered() {
|
|
@@ -461,6 +466,7 @@ export class PlayerManager {
|
|
|
461
466
|
if (this.castingReceiver) {
|
|
462
467
|
this.castingReceiver.closeNativePlayer();
|
|
463
468
|
} else {
|
|
469
|
+
this.getActivePlayer().closeNativePlayer();
|
|
464
470
|
this.invokeHandler("close");
|
|
465
471
|
}
|
|
466
472
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { NativeModules } from "react-native";
|
|
2
|
+
import { isApplePlatform } from "../../reactUtils";
|
|
3
|
+
|
|
4
|
+
export const RNPlayerManager = {
|
|
5
|
+
QuickBrickPlayerPlugin: isApplePlatform()
|
|
6
|
+
? NativeModules.PlayerModule
|
|
7
|
+
: NativeModules.QuickBrickDefaultPlayerModule,
|
|
8
|
+
KalturaPlayerPlugin: isApplePlatform()
|
|
9
|
+
? NativeModules.KalturaPlayerModule
|
|
10
|
+
: NativeModules.QBKalturaPlayerModule,
|
|
11
|
+
"quick-brick-player-brightcove": NativeModules.BrightcovePlayerModule,
|
|
12
|
+
};
|