@applicaster/zapp-react-native-utils 12.0.0-alpha.5895113034 → 12.0.0-alpha.5933014273
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 +2 -16
- 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 +40 -0
- package/appUtils/accessibilityManager/index.ts +166 -0
- package/appUtils/accessibilityManager/logger.ts +5 -0
- package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +2 -3
- package/appUtils/focusManager/__tests__/focusManager.test.js +4 -24
- package/appUtils/focusManager/__tests__/index.test.ts +2 -3
- package/appUtils/focusManager/index.ts +76 -57
- package/appUtils/focusManager/treeDataStructure/Utils/index.js +75 -44
- package/appUtils/focusManager/utils/index.ts +100 -0
- package/appUtils/focusManagerAux/index.ts +76 -0
- package/appUtils/focusManagerAux/utils/index.ts +100 -0
- 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 +217 -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 +14 -16
- package/appUtils/playerManager/playerHooks/usePlayerCurrentTime.tsx +57 -0
- package/appUtils/playerManager/playerNative.ts +5 -15
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +5 -18
- package/appUtils/playerManager/usePlayerState.tsx +19 -34
- package/arrayUtils/__tests__/isFilledArray.test.ts +63 -0
- package/arrayUtils/index.ts +5 -0
- package/booleanUtils/index.ts +4 -4
- package/cellUtils/index.ts +38 -5
- package/functionUtils/index.ts +38 -0
- package/index.d.ts +15 -0
- package/keyboardState/index.ts +0 -0
- package/navigationUtils/index.ts +48 -0
- package/numberUtils/__tests__/isLikeNumber.test.ts +18 -0
- package/numberUtils/__tests__/toNumber.test.ts +2 -2
- package/numberUtils/index.ts +5 -18
- package/package.json +2 -2
- package/playerUtils/imaScriptDom.ts +59 -0
- package/pluginUtils/index.ts +9 -0
- package/reactHooks/cell-click/__tests__/index.test.js +9 -5
- package/reactHooks/cell-click/index.ts +11 -11
- package/reactHooks/debugging/__tests__/index.test.js +81 -11
- package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +30 -70
- package/reactHooks/feed/__tests__/useFeedRefresh.test.tsx +6 -2
- package/reactHooks/feed/useBuildPipesUrl.ts +7 -10
- package/reactHooks/feed/useFeedRefresh.tsx +9 -7
- package/reactHooks/layout/useDimensions/useDimensions.ts +2 -0
- package/reactHooks/navigation/index.ts +44 -7
- package/reactHooks/player/__tests__/useAutoSeek.test.tsx +1 -8
- package/reactHooks/player/useAutoSeek.tsx +8 -28
- package/reactHooks/player/useSeek.tsx +2 -4
- package/reactHooks/player/useTapSeek.tsx +7 -34
- package/reactHooks/resolvers/useCellResolver.ts +5 -1
- package/reactHooks/screen/__tests__/useCurrentScreenData.test.tsx +21 -23
- package/reactHooks/screen/index.ts +2 -0
- package/reactHooks/screen/useScreenBackgroundColor.ts +23 -0
- package/reactHooks/screen/useScreenContext.ts +1 -2
- package/reactUtils/index.ts +104 -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
- package/appUtils/playerManager/DelayedSeekPlayerWrapper.tsx +0 -179
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { BehaviorSubject } from "rxjs";
|
|
2
|
+
|
|
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";
|
|
8
|
+
|
|
9
|
+
import { PLATFORM_KEYS, PLATFORMS } from "./const";
|
|
10
|
+
import { createLogger, utilsLogger } from "../../logger";
|
|
11
|
+
|
|
12
|
+
declare global {
|
|
13
|
+
interface Window {
|
|
14
|
+
VIZIO?: {
|
|
15
|
+
exitApplication: () => void;
|
|
16
|
+
Chromevox: {
|
|
17
|
+
play: (text: string) => void;
|
|
18
|
+
};
|
|
19
|
+
setClosedCaptionHandler: (
|
|
20
|
+
callback: (isCCEnabled: boolean) => void
|
|
21
|
+
) => void;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const { log_debug } = createLogger({
|
|
27
|
+
category: "General",
|
|
28
|
+
subsystem: "PlatformUtils",
|
|
29
|
+
parent: utilsLogger,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export const getUserAgent = () =>
|
|
33
|
+
window?.navigator?.userAgent || global?.navigator?.userAgent;
|
|
34
|
+
|
|
35
|
+
/** VIZIO UTILS */
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Checks if we are on the Vizio platform by using the userAgent string
|
|
39
|
+
*
|
|
40
|
+
* Use this method when you need to identify the platform on runtime
|
|
41
|
+
* before the VIZIO Companion Library has loaded
|
|
42
|
+
*/
|
|
43
|
+
export const isVizioPlatform = () => {
|
|
44
|
+
const userAgent = getUserAgent();
|
|
45
|
+
|
|
46
|
+
const isVizioAgent =
|
|
47
|
+
userAgent.includes(PLATFORMS.vizio) ||
|
|
48
|
+
userAgent.includes(PLATFORMS.smartcast) ||
|
|
49
|
+
userAgent.includes(PLATFORMS.conjure);
|
|
50
|
+
|
|
51
|
+
return isVizioAgent;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Checks if we are on the Vizio platform by checking for the global VIZIO object
|
|
56
|
+
* and the VIZIO Companion Library. This method does not use the userAgent string,
|
|
57
|
+
* because it mainly checks whether we have access to apis, which may not be available yet
|
|
58
|
+
*/
|
|
59
|
+
export const hasVizioAPIs = () => {
|
|
60
|
+
const hasAPIs =
|
|
61
|
+
typeof window.VIZIO !== "undefined" &&
|
|
62
|
+
window?.applicaster?.vizioLibraryDidLoad;
|
|
63
|
+
|
|
64
|
+
return hasAPIs;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Manages device based closed captioning state
|
|
69
|
+
* Returns an observable that emits the current state and updates whenever it changes
|
|
70
|
+
*/
|
|
71
|
+
export class ClosedCaptioningManager {
|
|
72
|
+
private ccState$ = new BehaviorSubject<boolean>(false);
|
|
73
|
+
private static ccManagerInstance: ClosedCaptioningManager;
|
|
74
|
+
|
|
75
|
+
private constructor() {
|
|
76
|
+
this.initialize();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
static getInstance(): ClosedCaptioningManager {
|
|
80
|
+
if (!ClosedCaptioningManager.ccManagerInstance) {
|
|
81
|
+
ClosedCaptioningManager.ccManagerInstance = new ClosedCaptioningManager();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return ClosedCaptioningManager.ccManagerInstance;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async initialize() {
|
|
88
|
+
const existingState = await sessionStorage.getItem(
|
|
89
|
+
PLATFORM_KEYS.deviceClosedCaptioningEnabled
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
log_debug("Initializing closed captioning manager", { existingState });
|
|
93
|
+
|
|
94
|
+
if (existingState !== null) {
|
|
95
|
+
this.ccState$.next(existingState === "true" || existingState === true);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (hasVizioAPIs()) {
|
|
99
|
+
window.VIZIO!.setClosedCaptionHandler((isCCEnabled) => {
|
|
100
|
+
log_debug("CC state changed", { isCCEnabled });
|
|
101
|
+
|
|
102
|
+
sessionStorage.setItem(
|
|
103
|
+
PLATFORM_KEYS.deviceClosedCaptioningEnabled,
|
|
104
|
+
isCCEnabled
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
this.ccState$.next(isCCEnabled);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
getCurrentState(): boolean {
|
|
113
|
+
return this.ccState$.getValue();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
getStateAsObservable() {
|
|
117
|
+
return this.ccState$.asObservable();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Subscribe to closed captioning state changes
|
|
123
|
+
* @returns Observable<boolean>
|
|
124
|
+
* @example
|
|
125
|
+
* const subscription = getClosedCaptionState().subscribe(
|
|
126
|
+
* isEnabled => setClosedCaptionState(isEnabled)
|
|
127
|
+
* );
|
|
128
|
+
* subscription.unsubscribe(), []);
|
|
129
|
+
*/
|
|
130
|
+
export const getClosedCaptionState = () => {
|
|
131
|
+
const ccManager = ClosedCaptioningManager.getInstance();
|
|
132
|
+
|
|
133
|
+
return ccManager.getStateAsObservable();
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Manages device based closed captioning state
|
|
138
|
+
* Returns an observable that emits the current state and updates whenever it changes
|
|
139
|
+
*/
|
|
140
|
+
export class TTSManager {
|
|
141
|
+
private ttsState$ = new BehaviorSubject<boolean>(false);
|
|
142
|
+
private static ttsManagerInstance: TTSManager;
|
|
143
|
+
|
|
144
|
+
private constructor() {
|
|
145
|
+
this.initialize();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
static getInstance(): TTSManager {
|
|
149
|
+
if (!TTSManager.ttsManagerInstance) {
|
|
150
|
+
TTSManager.ttsManagerInstance = new TTSManager();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return TTSManager.ttsManagerInstance;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async initialize() {
|
|
157
|
+
if (!isVizioPlatform()) return;
|
|
158
|
+
|
|
159
|
+
document.addEventListener(
|
|
160
|
+
"VIZIO_TTS_ENABLED",
|
|
161
|
+
() => {
|
|
162
|
+
this.ttsState$.next(true);
|
|
163
|
+
},
|
|
164
|
+
false
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
document.addEventListener(
|
|
168
|
+
"VIZIO_TTS_DISABLED",
|
|
169
|
+
() => {
|
|
170
|
+
this.ttsState$.next(false);
|
|
171
|
+
},
|
|
172
|
+
false
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
getCurrentState(): boolean {
|
|
177
|
+
return this.ttsState$.getValue();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
getStateAsObservable() {
|
|
181
|
+
return this.ttsState$.asObservable();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
readText(text: string) {
|
|
185
|
+
if (isSamsungPlatform() && window.speechSynthesis) {
|
|
186
|
+
const utterance = new SpeechSynthesisUtterance(text);
|
|
187
|
+
window.speechSynthesis.speak(utterance);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (isLgPlatform() && window.webOS?.service) {
|
|
191
|
+
try {
|
|
192
|
+
window.webOS.service.request("luna://com.webos.service.tts", {
|
|
193
|
+
method: "speak",
|
|
194
|
+
onFailure(error: any) {
|
|
195
|
+
log_debug("There was a failure setting up webOS TTS service", {
|
|
196
|
+
error,
|
|
197
|
+
});
|
|
198
|
+
},
|
|
199
|
+
onSuccess(response: any) {
|
|
200
|
+
log_debug("webOS TTS service is configured successfully", {
|
|
201
|
+
response,
|
|
202
|
+
});
|
|
203
|
+
},
|
|
204
|
+
parameters: {
|
|
205
|
+
text,
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
} catch (error) {
|
|
209
|
+
log_debug("webOS TTS service error", { error });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (!window.VIZIO?.Chromevox) return;
|
|
214
|
+
|
|
215
|
+
window.VIZIO.Chromevox.play(text);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from "@applicaster/quick-brick-core/App/NetworkStatusProvider/utils";
|
|
11
11
|
import { setUserLanguagePreference } from "./userLanguagePreference";
|
|
12
12
|
import { PlayerRole } from "./conts";
|
|
13
|
+
import { RNPlayerManager } from "./nativePlayerManager";
|
|
13
14
|
|
|
14
15
|
const { log_error, log_debug, log_warning } = createLogger({
|
|
15
16
|
category: "General",
|
|
@@ -168,6 +169,10 @@ export class PlayerManager {
|
|
|
168
169
|
this.addPlayer(id, playerController);
|
|
169
170
|
|
|
170
171
|
if (playerController.playerRole === PlayerRole.Primary) {
|
|
172
|
+
if (playerController.playerPluginId !== "QuickBrickPlayerPlugin") {
|
|
173
|
+
RNPlayerManager?.["QuickBrickPlayerPlugin"]?.stopBackgroundPlayback?.();
|
|
174
|
+
}
|
|
175
|
+
|
|
171
176
|
playerController.addListener({
|
|
172
177
|
id: this.getPlayerListenerId(id),
|
|
173
178
|
listener: {
|
|
@@ -343,9 +348,9 @@ export class PlayerManager {
|
|
|
343
348
|
// TODO: Remove
|
|
344
349
|
if (this.castingReceiver) {
|
|
345
350
|
this.castingReceiver.rewind(deltaTime);
|
|
351
|
+
} else {
|
|
352
|
+
this.getActivePlayer()?.rewind(deltaTime);
|
|
346
353
|
}
|
|
347
|
-
|
|
348
|
-
this.getActivePlayer()?.rewind(deltaTime);
|
|
349
354
|
}
|
|
350
355
|
|
|
351
356
|
public forward(deltaTime = null) {
|
|
@@ -353,9 +358,9 @@ export class PlayerManager {
|
|
|
353
358
|
|
|
354
359
|
if (this.castingReceiver) {
|
|
355
360
|
this.castingReceiver.forward(deltaTime);
|
|
361
|
+
} else {
|
|
362
|
+
this.getActivePlayer()?.forward(deltaTime);
|
|
356
363
|
}
|
|
357
|
-
|
|
358
|
-
this.getActivePlayer()?.forward(deltaTime);
|
|
359
364
|
}
|
|
360
365
|
|
|
361
366
|
public seeking(time) {
|
|
@@ -418,7 +423,7 @@ export class PlayerManager {
|
|
|
418
423
|
}
|
|
419
424
|
|
|
420
425
|
// Call method on player just in case
|
|
421
|
-
return this.getActivePlayer()?.
|
|
426
|
+
return this.getActivePlayer()?.selectTrack(track);
|
|
422
427
|
}
|
|
423
428
|
|
|
424
429
|
public isRegistered() {
|
|
@@ -443,6 +448,7 @@ export class PlayerManager {
|
|
|
443
448
|
if (this.castingReceiver) {
|
|
444
449
|
this.castingReceiver.closeNativePlayer();
|
|
445
450
|
} else {
|
|
451
|
+
this.getActivePlayer().closeNativePlayer();
|
|
446
452
|
this.invokeHandler("close");
|
|
447
453
|
}
|
|
448
454
|
}
|
|
@@ -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
|
+
};
|
|
@@ -91,8 +91,6 @@ export class Player {
|
|
|
91
91
|
this.onVideoProgress = this.onVideoProgress.bind(this);
|
|
92
92
|
this.onVideoLoad = this.onVideoLoad.bind(this);
|
|
93
93
|
this.isFullScreenSupported = this.isFullScreenSupported.bind(this);
|
|
94
|
-
this.onPlayerSeekComplete = this.onPlayerSeekComplete.bind(this);
|
|
95
|
-
|
|
96
94
|
this.entryObservable = null;
|
|
97
95
|
}
|
|
98
96
|
|
|
@@ -323,7 +321,7 @@ export class Player {
|
|
|
323
321
|
* You should use dispatchPlayerEvent at PlayerContainer or call playerManager.invokeHandler directly
|
|
324
322
|
* This allows you to use one method for everything.
|
|
325
323
|
*/
|
|
326
|
-
|
|
324
|
+
onSeekStart = (event: QuickBrickPlayer.OnSeekStartEvent) => {
|
|
327
325
|
const { isLive, seekableDuration, currentTime, duration, seekTime } = event;
|
|
328
326
|
|
|
329
327
|
this.setPlaybackState({ currentTime, duration, seekableDuration, isLive });
|
|
@@ -338,11 +336,10 @@ export class Player {
|
|
|
338
336
|
});
|
|
339
337
|
};
|
|
340
338
|
|
|
341
|
-
|
|
339
|
+
onSeekComplete = (event: QuickBrickPlayer.OnSeekCompleteEvent) => {
|
|
342
340
|
const { isLive, seekableDuration, currentTime, duration } = event;
|
|
343
341
|
|
|
344
342
|
this.setPlaybackState({ currentTime, duration, seekableDuration, isLive });
|
|
345
|
-
|
|
346
343
|
this.playerState.seekPosition = null;
|
|
347
344
|
|
|
348
345
|
if (!isLive) {
|
|
@@ -353,7 +350,7 @@ export class Player {
|
|
|
353
350
|
callbackName: SharedPlayerCallBacksKeys.OnPlayerSeekComplete,
|
|
354
351
|
event,
|
|
355
352
|
});
|
|
356
|
-
}
|
|
353
|
+
};
|
|
357
354
|
|
|
358
355
|
onVideoEnd = (event) => {
|
|
359
356
|
this.invokeListenersUpdate({
|
|
@@ -362,6 +359,13 @@ export class Player {
|
|
|
362
359
|
});
|
|
363
360
|
};
|
|
364
361
|
|
|
362
|
+
onVideoError = (error: Error) => {
|
|
363
|
+
this.invokeListenersUpdate({
|
|
364
|
+
callbackName: SharedPlayerCallBacksKeys.OnVideoError,
|
|
365
|
+
event: error,
|
|
366
|
+
});
|
|
367
|
+
};
|
|
368
|
+
|
|
365
369
|
onError = (error: Error) => {
|
|
366
370
|
this.invokeListenersUpdate({
|
|
367
371
|
callbackName: SharedPlayerCallBacksKeys.OnError,
|
|
@@ -649,10 +653,11 @@ export class Player {
|
|
|
649
653
|
};
|
|
650
654
|
|
|
651
655
|
public getListener = (): QuickBrickPlayer.SharedPlayerCallBacks => ({
|
|
652
|
-
onPlayerSeekStart: this.
|
|
653
|
-
onPlayerSeekComplete: this.
|
|
656
|
+
onPlayerSeekStart: this.onSeekStart,
|
|
657
|
+
onPlayerSeekComplete: this.onSeekComplete,
|
|
654
658
|
onVideoEnd: this.onVideoEnd,
|
|
655
659
|
onError: this.onError,
|
|
660
|
+
onVideoError: this.onVideoError,
|
|
656
661
|
onLoad: this.onLoad,
|
|
657
662
|
onPlayerPause: this.onPlayerPause,
|
|
658
663
|
onPlayerResume: this.onPlayerResume,
|
|
@@ -687,9 +692,6 @@ export class Player {
|
|
|
687
692
|
play = () => {};
|
|
688
693
|
pause = () => {};
|
|
689
694
|
disableBufferAnimation = (): boolean => true;
|
|
690
|
-
setLanguage = (
|
|
691
|
-
_selected?: QuickBrickPlayer.TextTrack | QuickBrickPlayer.AudioTrack
|
|
692
|
-
) => {};
|
|
693
695
|
|
|
694
696
|
setPlaybackRate = (_rate) => {};
|
|
695
697
|
startSleepTimer = (_timestamp) => {};
|
|
@@ -731,9 +733,5 @@ export class Player {
|
|
|
731
733
|
|
|
732
734
|
public supportsNativeControls = (): boolean => false;
|
|
733
735
|
public supportNativeCast = (): boolean => false;
|
|
734
|
-
|
|
735
|
-
public isInDelayedSeek = (): boolean => false;
|
|
736
|
-
public enterDelayedSeek() {}
|
|
737
|
-
|
|
738
|
-
public exitDelayedSeek() {}
|
|
736
|
+
public destroy = () => {};
|
|
739
737
|
}
|
|
@@ -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
|
+
};
|
|
@@ -2,19 +2,10 @@ import * as R from "ramda";
|
|
|
2
2
|
import { isEmptyOrNil } from "@applicaster/zapp-react-native-utils/cellUtils";
|
|
3
3
|
import { createLogger, utilsLogger } from "../../logger";
|
|
4
4
|
import { Player } from "./player";
|
|
5
|
-
import { findNodeHandle
|
|
6
|
-
import { isApplePlatform } from "../../reactUtils";
|
|
5
|
+
import { findNodeHandle } from "react-native";
|
|
7
6
|
import { PlayerRole } from "./conts";
|
|
8
7
|
import { toBooleanWithDefaultFalse } from "../../booleanUtils";
|
|
9
|
-
|
|
10
|
-
const RNPlayerManager = {
|
|
11
|
-
QuickBrickPlayerPlugin: isApplePlatform()
|
|
12
|
-
? NativeModules.PlayerModule
|
|
13
|
-
: NativeModules.QuickBrickDefaultPlayerModule,
|
|
14
|
-
KalturaPlayerPlugin: isApplePlatform()
|
|
15
|
-
? NativeModules.KalturaPlayerModule
|
|
16
|
-
: NativeModules.QBKalturaPlayerModule,
|
|
17
|
-
};
|
|
8
|
+
import { RNPlayerManager } from "./nativePlayerManager";
|
|
18
9
|
|
|
19
10
|
const logger = createLogger({
|
|
20
11
|
category: "PlayerNativeController",
|
|
@@ -197,10 +188,10 @@ export class PlayerNative extends Player {
|
|
|
197
188
|
|
|
198
189
|
disableBufferAnimation = (): boolean => false;
|
|
199
190
|
|
|
200
|
-
|
|
191
|
+
selectTrack = (
|
|
201
192
|
selected: QuickBrickPlayer.TextTrack | QuickBrickPlayer.AudioTrack
|
|
202
193
|
) => {
|
|
203
|
-
this.currentPlayerComponent()?.["
|
|
194
|
+
this.currentPlayerComponent()?.["selectTrack"]?.(selected);
|
|
204
195
|
};
|
|
205
196
|
|
|
206
197
|
setPlaybackRate = (rate: number) => {
|
|
@@ -231,10 +222,9 @@ export class PlayerNative extends Player {
|
|
|
231
222
|
|
|
232
223
|
onVideoLoad = (event) => {
|
|
233
224
|
if (this.startPosition) {
|
|
225
|
+
super.onVideoLoad({ ...event, ignoreContinueWatching: true });
|
|
234
226
|
this.seekTo(this.startPosition);
|
|
235
227
|
this.startPosition = null;
|
|
236
|
-
|
|
237
|
-
super.onVideoLoad({ ...event, ignoreContinueWatching: true });
|
|
238
228
|
} else {
|
|
239
229
|
super.onVideoLoad(event);
|
|
240
230
|
}
|
|
@@ -5,8 +5,6 @@ import { playerManager } from "./index";
|
|
|
5
5
|
import { useValidatePlayerConfig } from "../../playerUtils/useValidatePlayerConfig";
|
|
6
6
|
import { PlayerRole } from "./conts";
|
|
7
7
|
import { isAppleTV } from "@applicaster/zapp-react-native-ui-components/Helpers/Platform";
|
|
8
|
-
import { isTV } from "../../reactUtils";
|
|
9
|
-
import { DelayedSeekPlayerWrapper } from "./DelayedSeekPlayerWrapper";
|
|
10
8
|
|
|
11
9
|
// TODO: Rename to ControllerType
|
|
12
10
|
export const usePlayerControllerSetup = ({
|
|
@@ -41,7 +39,7 @@ export const usePlayerControllerSetup = ({
|
|
|
41
39
|
? { supports_native_controls: true, ...config }
|
|
42
40
|
: config;
|
|
43
41
|
|
|
44
|
-
const
|
|
42
|
+
const newPlayerController = new ControllerType({
|
|
45
43
|
player: ref,
|
|
46
44
|
listener: props as any,
|
|
47
45
|
config: newConfig,
|
|
@@ -49,22 +47,10 @@ export const usePlayerControllerSetup = ({
|
|
|
49
47
|
entry,
|
|
50
48
|
playerPluginId: pluginId,
|
|
51
49
|
playerRole: PlayerRole.Primary,
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
if (isTV()) {
|
|
55
|
-
const wrappedPlayerController = new ControllerType({
|
|
56
|
-
...combinedProps,
|
|
57
|
-
playerRole: PlayerRole.Unspecified,
|
|
58
|
-
playerId: playerId + "." + "wrapped",
|
|
59
|
-
});
|
|
50
|
+
});
|
|
60
51
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
);
|
|
64
|
-
} else {
|
|
65
|
-
setPlayerController(new ControllerType(combinedProps));
|
|
66
|
-
}
|
|
67
|
-
}, [playerId, entry?.id]);
|
|
52
|
+
setPlayerController(newPlayerController);
|
|
53
|
+
}, [playerId, entry?.id, ref.current]);
|
|
68
54
|
|
|
69
55
|
React.useEffect(() => {
|
|
70
56
|
if (!playerController) {
|
|
@@ -86,6 +72,7 @@ export const usePlayerControllerSetup = ({
|
|
|
86
72
|
|
|
87
73
|
return () => {
|
|
88
74
|
playerManager.unregisterPlayer(playerId);
|
|
75
|
+
playerController.destroy?.();
|
|
89
76
|
};
|
|
90
77
|
}, [playerId, playerController]);
|
|
91
78
|
|
|
@@ -16,24 +16,28 @@ export const usePlayerState = (
|
|
|
16
16
|
listenerId: string,
|
|
17
17
|
playerId?: string
|
|
18
18
|
): PlayerState => {
|
|
19
|
-
const [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
const [state, setState] = React.useState<PlayerState>({
|
|
20
|
+
currentTime: null,
|
|
21
|
+
duration: null,
|
|
22
|
+
seekableDuration: null,
|
|
23
|
+
isLive: null,
|
|
24
|
+
isPaused: null,
|
|
25
|
+
isBuffering: false,
|
|
26
|
+
isReadyToPlay: false,
|
|
27
|
+
});
|
|
26
28
|
|
|
27
29
|
const player: Player = usePlayer(playerId);
|
|
28
30
|
|
|
29
31
|
const onPlayerChangeState = React.useCallback(() => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
setState({
|
|
33
|
+
currentTime: player.getContentPosition(),
|
|
34
|
+
duration: player.getDuration(),
|
|
35
|
+
seekableDuration: player.getSeekableDuration(),
|
|
36
|
+
isLive: player.isLive(),
|
|
37
|
+
isPaused: player.isPaused(),
|
|
38
|
+
isBuffering: player.isBuffering(),
|
|
39
|
+
isReadyToPlay: player.isReadyToPlay(),
|
|
40
|
+
});
|
|
37
41
|
}, [player]);
|
|
38
42
|
|
|
39
43
|
React.useEffect(() => {
|
|
@@ -55,24 +59,5 @@ export const usePlayerState = (
|
|
|
55
59
|
}
|
|
56
60
|
}, [player]);
|
|
57
61
|
|
|
58
|
-
return
|
|
59
|
-
() => ({
|
|
60
|
-
currentTime,
|
|
61
|
-
duration,
|
|
62
|
-
seekableDuration,
|
|
63
|
-
isLive,
|
|
64
|
-
isPaused,
|
|
65
|
-
isBuffering,
|
|
66
|
-
isReadyToPlay,
|
|
67
|
-
}),
|
|
68
|
-
[
|
|
69
|
-
currentTime,
|
|
70
|
-
duration,
|
|
71
|
-
seekableDuration,
|
|
72
|
-
isLive,
|
|
73
|
-
isPaused,
|
|
74
|
-
isBuffering,
|
|
75
|
-
isReadyToPlay,
|
|
76
|
-
]
|
|
77
|
-
);
|
|
62
|
+
return state;
|
|
78
63
|
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { isFilledArray } from "..";
|
|
2
|
+
|
|
3
|
+
describe("isFilledArray", () => {
|
|
4
|
+
it("non-empty array is not empty", () => {
|
|
5
|
+
const value = [1, 2, 3];
|
|
6
|
+
|
|
7
|
+
expect(isFilledArray(value)).toBe(true);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it("empty array is empty", () => {
|
|
11
|
+
const value = [];
|
|
12
|
+
|
|
13
|
+
expect(isFilledArray(value)).toBe(false);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("number is not array", () => {
|
|
17
|
+
const value = 123;
|
|
18
|
+
|
|
19
|
+
expect(isFilledArray(value)).toBe(false);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("string is not array", () => {
|
|
23
|
+
const value = "vfnjdk";
|
|
24
|
+
|
|
25
|
+
expect(isFilledArray(value)).toBe(false);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("empty string is not array", () => {
|
|
29
|
+
const value = "";
|
|
30
|
+
|
|
31
|
+
expect(isFilledArray(value)).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("NaN is not array", () => {
|
|
35
|
+
const value = "";
|
|
36
|
+
|
|
37
|
+
expect(isFilledArray(value)).toBe(false);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("object is not array", () => {
|
|
41
|
+
const value = { test: 1 };
|
|
42
|
+
|
|
43
|
+
expect(isFilledArray(value)).toBe(false);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("empty object is not array", () => {
|
|
47
|
+
const value = {};
|
|
48
|
+
|
|
49
|
+
expect(isFilledArray(value)).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("undefined is not array", () => {
|
|
53
|
+
const value = undefined;
|
|
54
|
+
|
|
55
|
+
expect(isFilledArray(value)).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("null is not array", () => {
|
|
59
|
+
const value = null;
|
|
60
|
+
|
|
61
|
+
expect(isFilledArray(value)).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
});
|
package/arrayUtils/index.ts
CHANGED
|
@@ -95,3 +95,8 @@ export const makeListOfIndexes = (size: number): number[] =>
|
|
|
95
95
|
export const makeListOf = (value: unknown, size: number): number[] => {
|
|
96
96
|
return Array(size).fill(value);
|
|
97
97
|
};
|
|
98
|
+
|
|
99
|
+
/** Checks if a value is a non-empty array */
|
|
100
|
+
export function isFilledArray(value: unknown): boolean {
|
|
101
|
+
return R.is(Array, value) && R.length(value) > 0;
|
|
102
|
+
}
|