@applicaster/zapp-react-native-utils 12.0.0-rc.99 → 12.0.1
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/AnalyticPlayerListener.ts +141 -0
- package/analyticsUtils/PlayerAnalyticsManager.ts +90 -0
- package/analyticsUtils/{analyticsTracker.ts → playerAnalyticsTracker.ts} +19 -10
- package/appUtils/accessibilityManager/hooks.ts +16 -7
- package/appUtils/accessibilityManager/index.ts +33 -28
- package/appUtils/platform/platformUtils.ts +35 -2
- package/appUtils/playerManager/index.ts +9 -27
- package/appUtils/playerManager/playerFactory.ts +5 -4
- package/appUtils/playerManager/playerHooks/usePlayerCurrentTime.tsx +57 -0
- package/appUtils/playerManager/playerNative.ts +1 -1
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +1 -1
- package/appUtils/playerManager/usePlayerState.tsx +19 -34
- package/arrayUtils/index.ts +11 -0
- package/cellUtils/index.ts +38 -5
- package/focusManager/FocusManager.ts +20 -5
- package/focusManager/__tests__/FocusManager.test.ts +0 -1
- package/functionUtils/index.ts +38 -0
- package/index.d.ts +15 -0
- package/manifestUtils/defaultManifestConfigurations/player.js +121 -0
- package/manifestUtils/keys.js +2 -1
- package/navigationUtils/index.ts +1 -1
- package/numberUtils/__tests__/toNumber.test.ts +2 -2
- package/numberUtils/__tests__/toPositiveNumberWithDefault.test.ts +66 -0
- package/numberUtils/__tests__/toPositiveNumberWithDefaultZero.test.ts +66 -0
- package/numberUtils/index.ts +20 -0
- package/package.json +2 -2
- package/playerUtils/configurationGenerator.ts +120 -0
- package/playerUtils/imaScriptDom.ts +59 -0
- package/reactHooks/cell-click/index.ts +17 -3
- package/reactHooks/debugging/__tests__/index.test.js +81 -11
- package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +69 -6
- package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +10 -2
- package/reactHooks/feed/useBatchLoading.ts +52 -28
- package/reactHooks/feed/useFeedLoader.tsx +11 -7
- package/reactHooks/layout/useDimensions/__tests__/useDimensions.test.ts +134 -0
- package/reactHooks/layout/useDimensions/useDimensions.ts +2 -0
- package/reactHooks/navigation/index.ts +1 -1
- package/reactHooks/player/useAutoSeek.tsx +9 -7
- package/reactHooks/resolvers/useCellResolver.ts +5 -1
- package/reactHooks/useConfiguration.ts +2 -2
- package/reactHooks/utils/index.ts +13 -0
- package/reactUtils/index.ts +1 -2
- package/services/localStorageServiceSync.web.ts +48 -0
- package/services/sessionStorageServiceSync.web.ts +32 -0
- package/services/storageServiceSync.web.ts +24 -0
- package/services/storageWebUtils.web.ts +10 -0
- package/testUtils/index.tsx +42 -9
|
@@ -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
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -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
|
|
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
|
-
|
|
17
|
-
this.
|
|
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.
|
|
223
|
-
this.playerState = playerState;
|
|
224
|
-
|
|
233
|
+
this.playerState = this.player?.getState();
|
|
225
234
|
postAnalyticEvent(event, this.prepareAnalyticPayload(event, eventData));
|
|
226
235
|
}
|
|
227
236
|
}
|
|
@@ -1,18 +1,27 @@
|
|
|
1
1
|
import { useEffect, useMemo } from "react";
|
|
2
|
-
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
3
2
|
import { AccessibilityManager } from "./index";
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
+
) => {
|
|
9
16
|
const accessibilityManager = useMemo(() => {
|
|
10
17
|
return AccessibilityManager.getInstance();
|
|
11
18
|
}, []);
|
|
12
19
|
|
|
13
20
|
useEffect(() => {
|
|
14
|
-
|
|
15
|
-
|
|
21
|
+
if (pluginConfiguration) {
|
|
22
|
+
accessibilityManager.updateLocalizations(pluginConfiguration);
|
|
23
|
+
}
|
|
24
|
+
}, [pluginConfiguration, accessibilityManager]);
|
|
16
25
|
|
|
17
26
|
useEffect(() => {
|
|
18
27
|
const subscription = accessibilityManager.getStateAsObservable().subscribe({
|
|
@@ -1,21 +1,8 @@
|
|
|
1
1
|
import { BehaviorSubject } from "rxjs";
|
|
2
2
|
import { accessibilityManagerLogger as logger } from "./logger";
|
|
3
3
|
import { TTSManager } from "../platform/platformUtils";
|
|
4
|
-
import { getLocalizations } from "../../localizationUtils";
|
|
5
4
|
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
|
-
}
|
|
5
|
+
import { AccessibilityRole } from "react-native";
|
|
19
6
|
|
|
20
7
|
export class AccessibilityManager {
|
|
21
8
|
private static _instance: AccessibilityManager | null = null;
|
|
@@ -32,13 +19,9 @@ export class AccessibilityManager {
|
|
|
32
19
|
} | null>(null);
|
|
33
20
|
|
|
34
21
|
private ttsManager = TTSManager.getInstance();
|
|
35
|
-
private localizations: { [key: string]: string };
|
|
22
|
+
private localizations: { [key: string]: string } = {};
|
|
36
23
|
|
|
37
|
-
private constructor(
|
|
38
|
-
if (localizations) {
|
|
39
|
-
this.localizations = getLocalizations({ localizations });
|
|
40
|
-
}
|
|
41
|
-
}
|
|
24
|
+
private constructor() {}
|
|
42
25
|
|
|
43
26
|
public static getInstance(): AccessibilityManager {
|
|
44
27
|
if (!AccessibilityManager._instance) {
|
|
@@ -48,8 +31,17 @@ export class AccessibilityManager {
|
|
|
48
31
|
return AccessibilityManager._instance;
|
|
49
32
|
}
|
|
50
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
|
+
*/
|
|
51
43
|
public updateLocalizations(localizations: { [key: string]: string }) {
|
|
52
|
-
this.localizations =
|
|
44
|
+
this.localizations = localizations;
|
|
53
45
|
}
|
|
54
46
|
|
|
55
47
|
public getState(): AccessibilityState {
|
|
@@ -71,7 +63,7 @@ export class AccessibilityManager {
|
|
|
71
63
|
text: string;
|
|
72
64
|
keyOfLocalizedText?: string;
|
|
73
65
|
}) {
|
|
74
|
-
|
|
66
|
+
let textToRead = text;
|
|
75
67
|
|
|
76
68
|
if (keyOfLocalizedText) {
|
|
77
69
|
if (!this.localizations) {
|
|
@@ -90,16 +82,14 @@ export class AccessibilityManager {
|
|
|
90
82
|
return;
|
|
91
83
|
}
|
|
92
84
|
|
|
93
|
-
|
|
85
|
+
textToRead = localizedMessage;
|
|
94
86
|
}
|
|
95
87
|
|
|
96
|
-
|
|
97
|
-
this.ttsManager.readText(text);
|
|
98
|
-
}
|
|
88
|
+
this.ttsManager && this.ttsManager.readText(textToRead);
|
|
99
89
|
|
|
100
90
|
this.announcements$.next({
|
|
101
|
-
message:
|
|
102
|
-
localizedMessage: keyOfLocalizedText ?
|
|
91
|
+
message: textToRead,
|
|
92
|
+
localizedMessage: keyOfLocalizedText ? textToRead : undefined,
|
|
103
93
|
timestamp: Date.now(),
|
|
104
94
|
});
|
|
105
95
|
}
|
|
@@ -113,6 +103,8 @@ export class AccessibilityManager {
|
|
|
113
103
|
accessibilityHint: `Press button to perform action on ${buttonName}`,
|
|
114
104
|
"aria-label": buttonName,
|
|
115
105
|
"aria-description": `Press button to perform action on ${buttonName}`,
|
|
106
|
+
accessibilityRole: "button" as AccessibilityRole,
|
|
107
|
+
"aria-role": "button",
|
|
116
108
|
};
|
|
117
109
|
}
|
|
118
110
|
|
|
@@ -130,6 +122,19 @@ export class AccessibilityManager {
|
|
|
130
122
|
accessibilityHint: hint,
|
|
131
123
|
"aria-label": label,
|
|
132
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",
|
|
133
138
|
};
|
|
134
139
|
}
|
|
135
140
|
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { BehaviorSubject } from "rxjs";
|
|
2
2
|
|
|
3
3
|
import { sessionStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/SessionStorage";
|
|
4
|
+
import {
|
|
5
|
+
isLgPlatform,
|
|
6
|
+
isSamsungPlatform,
|
|
7
|
+
} from "@applicaster/zapp-react-dom-app/App/Loader/utils/platform";
|
|
4
8
|
|
|
5
9
|
import { PLATFORM_KEYS, PLATFORMS } from "./const";
|
|
6
10
|
import { createLogger, utilsLogger } from "../../logger";
|
|
@@ -25,8 +29,7 @@ const { log_debug } = createLogger({
|
|
|
25
29
|
parent: utilsLogger,
|
|
26
30
|
});
|
|
27
31
|
|
|
28
|
-
export const getUserAgent = () =>
|
|
29
|
-
window?.navigator?.userAgent || global?.navigator?.userAgent;
|
|
32
|
+
export const getUserAgent = () => window?.navigator?.userAgent;
|
|
30
33
|
|
|
31
34
|
/** VIZIO UTILS */
|
|
32
35
|
|
|
@@ -39,6 +42,8 @@ export const getUserAgent = () =>
|
|
|
39
42
|
export const isVizioPlatform = () => {
|
|
40
43
|
const userAgent = getUserAgent();
|
|
41
44
|
|
|
45
|
+
if (!userAgent) return false;
|
|
46
|
+
|
|
42
47
|
const isVizioAgent =
|
|
43
48
|
userAgent.includes(PLATFORMS.vizio) ||
|
|
44
49
|
userAgent.includes(PLATFORMS.smartcast) ||
|
|
@@ -178,6 +183,34 @@ export class TTSManager {
|
|
|
178
183
|
}
|
|
179
184
|
|
|
180
185
|
readText(text: string) {
|
|
186
|
+
if (isSamsungPlatform() && window.speechSynthesis) {
|
|
187
|
+
const utterance = new SpeechSynthesisUtterance(text);
|
|
188
|
+
window.speechSynthesis.speak(utterance);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (isLgPlatform() && window.webOS?.service) {
|
|
192
|
+
try {
|
|
193
|
+
window.webOS.service.request("luna://com.webos.service.tts", {
|
|
194
|
+
method: "speak",
|
|
195
|
+
onFailure(error: any) {
|
|
196
|
+
log_debug("There was a failure setting up webOS TTS service", {
|
|
197
|
+
error,
|
|
198
|
+
});
|
|
199
|
+
},
|
|
200
|
+
onSuccess(response: any) {
|
|
201
|
+
log_debug("webOS TTS service is configured successfully", {
|
|
202
|
+
response,
|
|
203
|
+
});
|
|
204
|
+
},
|
|
205
|
+
parameters: {
|
|
206
|
+
text,
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
} catch (error) {
|
|
210
|
+
log_debug("webOS TTS service error", { error });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
181
214
|
if (!window.VIZIO?.Chromevox) return;
|
|
182
215
|
|
|
183
216
|
window.VIZIO.Chromevox.play(text);
|
|
@@ -3,7 +3,6 @@ import * as R from "ramda";
|
|
|
3
3
|
import { subscriber } from "../../functionUtils";
|
|
4
4
|
import { utilsLogger, createLogger } from "../../logger";
|
|
5
5
|
import { Player } from "./player";
|
|
6
|
-
import { AnalyticsTracker } from "../../analyticsUtils/analyticsTracker";
|
|
7
6
|
import {
|
|
8
7
|
hasTizenAPIs,
|
|
9
8
|
subscribeToDeviceNetworkChanges,
|
|
@@ -37,7 +36,10 @@ export interface PlayerLifecycleListener {
|
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
type PlayersMap = {
|
|
40
|
-
[id: string]: {
|
|
39
|
+
[id: string]: {
|
|
40
|
+
player: Player;
|
|
41
|
+
isInViewport: boolean;
|
|
42
|
+
};
|
|
41
43
|
};
|
|
42
44
|
|
|
43
45
|
export class PlayerManager {
|
|
@@ -46,7 +48,6 @@ export class PlayerManager {
|
|
|
46
48
|
private playersMap: PlayersMap = {};
|
|
47
49
|
private manager = subscriber();
|
|
48
50
|
private castingReceiver: Player;
|
|
49
|
-
private analyticsTracker: AnalyticsTracker;
|
|
50
51
|
private lifecycleListeners: PlayerLifecycleListener[] = [];
|
|
51
52
|
|
|
52
53
|
public static getInstance() {
|
|
@@ -57,7 +58,7 @@ export class PlayerManager {
|
|
|
57
58
|
return PlayerManager.instance;
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
public getActivePlayerId = ():
|
|
61
|
+
public getActivePlayerId = (): string | null => {
|
|
61
62
|
return this.activePlayerId;
|
|
62
63
|
};
|
|
63
64
|
|
|
@@ -102,7 +103,10 @@ export class PlayerManager {
|
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
private addPlayer = (id: string, player: Player) => {
|
|
105
|
-
this.playersMap[id] = {
|
|
106
|
+
this.playersMap[id] = {
|
|
107
|
+
player: player,
|
|
108
|
+
isInViewport: false,
|
|
109
|
+
};
|
|
106
110
|
};
|
|
107
111
|
|
|
108
112
|
public isPlayerRegistered = (id: string) => !!this.playersMap[id];
|
|
@@ -251,28 +255,6 @@ export class PlayerManager {
|
|
|
251
255
|
this.castingReceiver = null;
|
|
252
256
|
}
|
|
253
257
|
|
|
254
|
-
/**
|
|
255
|
-
* Handles analytic events, generates a new analytics tracker and transmits events
|
|
256
|
-
* Passes entry data, and playerState to the tracker for additional context
|
|
257
|
-
* The end result is an event formatted and passed to AnalyticsBridge.postEvent
|
|
258
|
-
* When we get the synthetic event we need to pull the data in the e object
|
|
259
|
-
* e contains the return value from native
|
|
260
|
-
*/
|
|
261
|
-
public handleAnalyticEvent(event, data = {}, entry: ZappEntry) {
|
|
262
|
-
const playerState = this.getActivePlayer()?.getState();
|
|
263
|
-
|
|
264
|
-
if (!this.analyticsTracker?.handleAnalyticEvent) {
|
|
265
|
-
this.analyticsTracker = new AnalyticsTracker(entry);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
this.analyticsTracker?.handleAnalyticEvent?.(
|
|
269
|
-
event,
|
|
270
|
-
data,
|
|
271
|
-
entry,
|
|
272
|
-
playerState
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
258
|
/**
|
|
277
259
|
* Method retrieves the instantiated player instance controller
|
|
278
260
|
* Instance controller exposes methods like getContentDuration, getContentPosision
|
|
@@ -40,13 +40,14 @@ export const playerFactory = (
|
|
|
40
40
|
|
|
41
41
|
const playerPlugin = findPluginByIdentifier(IDENTIFIER, plugins);
|
|
42
42
|
|
|
43
|
-
if (!playerPlugin) {
|
|
43
|
+
if (!playerPlugin?.module?.playerProtocol) {
|
|
44
44
|
return null;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
const playerProtocol: PlayerConstructor = playerPlugin.module.playerProtocol(
|
|
48
|
+
config?.screenConfig,
|
|
49
|
+
config?.entry
|
|
50
|
+
);
|
|
50
51
|
|
|
51
52
|
const playerView = (playerProtocol as any)?.Component || null;
|
|
52
53
|
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback } from "react";
|
|
2
|
+
import { Player } from "../player";
|
|
3
|
+
import { usePlayer } from "../usePlayer";
|
|
4
|
+
import { useThrottle } from "../../../functionUtils";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* @param listenerId - A unique identifier for the listener.
|
|
9
|
+
* @param playerId - (Optional) The ID of the player to observe.
|
|
10
|
+
* @returns The current playback time in seconds.
|
|
11
|
+
*/
|
|
12
|
+
export const usePlayerCurrentTime = (
|
|
13
|
+
listenerId: string,
|
|
14
|
+
playerId?: string
|
|
15
|
+
): number | null => {
|
|
16
|
+
const [currentTime, setCurrentTime] = useState<number | null>(null);
|
|
17
|
+
const player: Player = usePlayer(playerId);
|
|
18
|
+
|
|
19
|
+
const updateCurrentTime = useCallback(() => {
|
|
20
|
+
if (player) {
|
|
21
|
+
const newCurrentTime = player.getContentPosition();
|
|
22
|
+
|
|
23
|
+
setCurrentTime((prevTime) =>
|
|
24
|
+
prevTime !== newCurrentTime ? newCurrentTime : prevTime
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
}, [player]);
|
|
28
|
+
|
|
29
|
+
const { throttledFunction: throttledUpdate, cancel } = useThrottle(
|
|
30
|
+
updateCurrentTime,
|
|
31
|
+
1000
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (player) {
|
|
36
|
+
throttledUpdate();
|
|
37
|
+
|
|
38
|
+
const listeners = {
|
|
39
|
+
onVideoProgress: throttledUpdate,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const id = `${listenerId}_player_current_time`;
|
|
43
|
+
|
|
44
|
+
player.addListener({
|
|
45
|
+
id: id,
|
|
46
|
+
listener: listeners,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return () => {
|
|
50
|
+
player.removeListener(id);
|
|
51
|
+
cancel();
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}, [player, listenerId, throttledUpdate, cancel]);
|
|
55
|
+
|
|
56
|
+
return currentTime;
|
|
57
|
+
};
|
|
@@ -61,7 +61,7 @@ export class PlayerNative extends Player {
|
|
|
61
61
|
currentPlayerComponent = (): ReactComponent<PlayerProps> =>
|
|
62
62
|
this.playerComponent?.current;
|
|
63
63
|
|
|
64
|
-
getState = () => this.
|
|
64
|
+
getState = () => this.playerState;
|
|
65
65
|
|
|
66
66
|
// TODO: Try to add module interface
|
|
67
67
|
invokeNativeFunction = (funcName: string, ...params: any[]): boolean => {
|