@applicaster/zapp-react-native-utils 13.0.0-alpha.7210171818 → 13.0.0-alpha.7846284820
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/actionsExecutor/ActionExecutor.ts +16 -9
- package/actionsExecutor/ActionExecutorContext.tsx +74 -9
- package/alertUtils/alert.ts +148 -0
- package/alertUtils/index.ts +1 -148
- package/analyticsUtils/AnalyticPlayerListener.ts +18 -1
- package/analyticsUtils/__tests__/__snapshots__/analyticsManagerNoModule.test.js.snap +9 -9
- package/analyticsUtils/helpers/hooks.ts +1 -1
- package/appUtils/HooksManager/{Hook.js → Hook.ts} +35 -23
- package/appUtils/HooksManager/__tests__/__snapshots__/hook.test.js.snap +10 -8
- package/appUtils/HooksManager/__tests__/__snapshots__/hooksManager.test.js.snap +693 -0
- package/appUtils/HooksManager/__tests__/hook.test.js +196 -184
- package/appUtils/HooksManager/__tests__/hooksManager.test.js +96 -99
- package/appUtils/HooksManager/{constants.js → constants.ts} +1 -1
- package/appUtils/HooksManager/{index.js → index.ts} +121 -112
- package/appUtils/HooksManager/types.ts +119 -0
- package/appUtils/RiverFocusManager/index.js +17 -12
- package/appUtils/accessibilityManager/index.ts +42 -1
- package/appUtils/contextKeysManager/contextResolver.ts +77 -0
- package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +6 -6
- package/appUtils/focusManager/index.ts +91 -65
- package/appUtils/focusManager/treeDataStructure/Tree/index.js +25 -3
- package/appUtils/focusManager/treeDataStructure/Utils/index.js +1 -3
- package/appUtils/focusManager/utils/enums.ts +4 -0
- package/appUtils/focusManagerAux/utils/index.ts +4 -3
- package/appUtils/{index.js → index.ts} +1 -1
- package/appUtils/keyCodes/__tests__/__snapshots__/keyCodes.test.js.snap +2 -2
- package/appUtils/keyCodes/__tests__/keyCodes.test.js +4 -4
- package/appUtils/keyCodes/{index.js → index.ts} +2 -0
- package/appUtils/keyCodes/keys/index.ts +3 -0
- package/appUtils/keyCodes/{keys.android.js → keys/keyEventMapper.ts} +0 -19
- package/appUtils/keyCodes/keys/keys.android.ts +18 -0
- package/appUtils/keyCodes/keys/keys.ios.ts +25 -0
- package/appUtils/keyCodes/keys/keys.ts +15 -0
- package/appUtils/keyCodes/{keysByPlatform.js → keys/keysByPlatform.ts} +4 -3
- package/appUtils/keyCodes/keysByPlatform.ts +8 -0
- package/appUtils/keyInputHandler/KeyInputHandler.ts +10 -32
- package/appUtils/orientationHelper.ts +1 -1
- package/appUtils/platform/const.ts +14 -0
- package/appUtils/platform/platformUtils.ts +17 -15
- package/appUtils/playerManager/index.ts +6 -2
- package/appUtils/playerManager/player.ts +9 -10
- package/appUtils/playerManager/playerNative.ts +1 -1
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +1 -15
- package/arrayUtils/__tests__/__snapshots__/arrayUtils.test.ts.snap +4 -4
- package/domUtils/{cookieManager.js → cookieManager.ts} +1 -2
- package/functionUtils/__tests__/__snapshots__/functionUtils.test.js.snap +4 -4
- package/functionUtils/index.ts +15 -11
- package/idleUtils/__tests__/withTimeout$.test.ts +37 -0
- package/idleUtils/index.ts +1 -1
- package/idleUtils/withTimeout.ts +15 -2
- package/logger/const.ts +9 -67
- package/manifestUtils/__tests__/createConfig.test.js +18 -16
- package/modalState/index.ts +1 -5
- package/navigationUtils/__tests__/__snapshots__/navigationUtils.test.js.snap +6 -6
- package/navigationUtils/__tests__/itemTypes.test.js +2 -2
- package/navigationUtils/__tests__/navigationUtils.test.js +2 -2
- package/navigationUtils/index.ts +17 -8
- package/package.json +3 -2
- package/reactHooks/autoscrolling/__tests__/useTrackCurrentAutoScrollingElement.test.ts +1 -1
- package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +1 -1
- package/reactHooks/cell-click/__tests__/index.test.js +10 -2
- package/reactHooks/cell-click/index.ts +7 -1
- package/reactHooks/feed/__tests__/useBuildPipesUrl.test.tsx +4 -4
- package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +10 -11
- package/reactHooks/feed/useFeedLoader.tsx +1 -1
- package/reactHooks/feed/usePipesCacheReset.ts +5 -5
- package/reactHooks/flatList/__tests__/index.test.tsx +30 -21
- package/reactHooks/layout/__tests__/index.test.tsx +10 -10
- package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.native.test.js +14 -28
- package/reactHooks/layout/useDimensions/helpers/__tests__/getInitialDimensions.test.js +1 -1
- package/reactHooks/navigation/__tests__/index.test.tsx +0 -4
- package/reactHooks/navigation/index.ts +29 -218
- package/reactHooks/navigation/useGetBottomTabBarHeight.ts +12 -0
- package/reactHooks/navigation/useGetNavBarTopBorderWidth.ts +14 -0
- package/reactHooks/navigation/useGetTabBarHeight.ts +12 -0
- package/reactHooks/navigation/useIsScreenActive.ts +17 -0
- package/reactHooks/navigation/useNavigationPluginData.ts +15 -0
- package/reactHooks/navigation/useNavigationType.ts +15 -0
- package/reactHooks/navigation/usePathname.ts +4 -0
- package/reactHooks/navigation/useRoute.ts +100 -0
- package/reactHooks/navigation/useUniqueRouteSuffix.ts +41 -0
- package/reactHooks/player/TVSeekControlller/ISeekController.ts +27 -2
- package/reactHooks/player/TVSeekControlller/LongPressSeekController.ts +39 -74
- package/reactHooks/player/TVSeekControlller/TVSeekController.ts +155 -96
- package/reactHooks/player/TVSeekControlller/TapSeekController.ts +31 -53
- package/reactHooks/player/__tests__/useAutoSeek._test.tsx +115 -0
- package/reactHooks/player/__tests__/useTapSeek._test.ts +94 -0
- package/reactHooks/player/const.ts +1 -5
- package/reactHooks/usePluginConfiguration.ts +1 -1
- package/reactHooks/useSubscriberFor.ts +4 -3
- package/reactUtils/__tests__/reactUtils.test.js +10 -28
- package/reactUtils/createContext/__tests__/__snapshots__/helpers.test.js.snap +19 -19
- package/reactUtils/createContext/__tests__/__snapshots__/index.test.js.snap +2 -2
- package/reactUtils/createContext/__tests__/__snapshots__/provider.test.js.snap +3 -3
- package/reactUtils/createContext/__tests__/helpers.test.js +8 -8
- package/reactUtils/index.ts +0 -14
- package/screenState/index.ts +24 -10
- package/theme/__mocks__/index.ts +4 -0
- package/theme/index.ts +1 -1
- package/time/Timer.ts +5 -2
- package/types.ts +1 -0
- package/zappFrameworkUtils/presentPluginOneTime.ts +2 -3
- package/appUtils/keyCodes/keys.ios.js +0 -46
- package/appUtils/playerManager/DelayedSeekPlayerWrapper.tsx +0 -180
- /package/appUtils/HooksManager/{logger.js → logger.ts} +0 -0
- /package/appUtils/{directionHelper.js → directionHelper.ts} +0 -0
- /package/appUtils/keyCodes/{androidTVKeys.js → androidTVKeys.ts} +0 -0
- /package/appUtils/keyCodes/{keys.web.js → keys/keys.web.ts} +0 -0
- /package/appUtils/{localizationsHelper.js → localizationsHelper.ts} +0 -0
- /package/domUtils/{logger.js → logger.ts} +0 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { isWeb } from "../../reactUtils";
|
|
3
|
+
import { useNavigation } from "./useNavigation";
|
|
4
|
+
import { usePathname } from "./usePathname";
|
|
5
|
+
import {
|
|
6
|
+
QUICK_BRICK_CONTENT,
|
|
7
|
+
QUICK_BRICK_NAVBAR,
|
|
8
|
+
} from "@applicaster/quick-brick-core/const";
|
|
9
|
+
|
|
10
|
+
type Return = (prefix: string) => string;
|
|
11
|
+
|
|
12
|
+
export const useUniqueRouteSuffix = (): Return => {
|
|
13
|
+
const pathname = usePathname();
|
|
14
|
+
const { currentRoute } = useNavigation();
|
|
15
|
+
|
|
16
|
+
const suffix = `currentRoute:${currentRoute}_pathname:${pathname}`;
|
|
17
|
+
|
|
18
|
+
const withUniqueRouteSuffix = React.useCallback((prefix: string) => {
|
|
19
|
+
if (isWeb()) {
|
|
20
|
+
// we should use extended route prefix only on Web, where we have stack-navigator
|
|
21
|
+
return `${prefix}___${suffix}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// for other platforms use previous version
|
|
25
|
+
return prefix;
|
|
26
|
+
}, []);
|
|
27
|
+
|
|
28
|
+
return withUniqueRouteSuffix;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const useNavbarId = () => {
|
|
32
|
+
const withUniqueRouteSuffix = useUniqueRouteSuffix();
|
|
33
|
+
|
|
34
|
+
return withUniqueRouteSuffix(QUICK_BRICK_NAVBAR);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const useContentId = () => {
|
|
38
|
+
const withUniqueRouteSuffix = useUniqueRouteSuffix();
|
|
39
|
+
|
|
40
|
+
return withUniqueRouteSuffix(QUICK_BRICK_CONTENT);
|
|
41
|
+
};
|
|
@@ -1,7 +1,32 @@
|
|
|
1
|
-
import { SEEK_TYPE } from "../const";
|
|
1
|
+
import { MAX_SEEK_TIME, SEEK_TYPE, SKIP_TIME_BASE } from "../const";
|
|
2
|
+
|
|
3
|
+
export interface ISeekControllerDelegate {
|
|
4
|
+
handleDelayedSeek(offset: number): void;
|
|
5
|
+
handleSeek(offset: number): void;
|
|
6
|
+
handleSeekComplete(): void;
|
|
7
|
+
handlePlayerPause(): void;
|
|
8
|
+
}
|
|
2
9
|
|
|
3
10
|
export interface ISeekController {
|
|
11
|
+
title: string;
|
|
4
12
|
onStartSeek(seekType: SEEK_TYPE): void;
|
|
5
|
-
onSeekEnd
|
|
13
|
+
onSeekEnd(): void;
|
|
6
14
|
reset(): void;
|
|
7
15
|
}
|
|
16
|
+
|
|
17
|
+
export class SeekIncrementalBaseStrategy {
|
|
18
|
+
private skipMultiplier = 1;
|
|
19
|
+
|
|
20
|
+
getSkipTime = () =>
|
|
21
|
+
Math.min(MAX_SEEK_TIME, SKIP_TIME_BASE * this.skipMultiplier);
|
|
22
|
+
|
|
23
|
+
increment = () => {
|
|
24
|
+
this.skipMultiplier++;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
getSkipMultiplier = () => this.skipMultiplier;
|
|
28
|
+
|
|
29
|
+
reset = () => {
|
|
30
|
+
this.skipMultiplier = 1;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -1,99 +1,64 @@
|
|
|
1
|
-
import { ISeekController } from "./ISeekController";
|
|
2
|
-
import { SEEK_TYPE } from "../const";
|
|
3
1
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} from "
|
|
2
|
+
ISeekController,
|
|
3
|
+
ISeekControllerDelegate,
|
|
4
|
+
SeekIncrementalBaseStrategy,
|
|
5
|
+
} from "./ISeekController";
|
|
6
|
+
import { SEEK_TYPE } from "../const";
|
|
7
|
+
import { log_debug, log_error, log_verbose } from "./TVSeekController";
|
|
9
8
|
import { interval, Subscription } from "rxjs";
|
|
10
9
|
import { tap } from "rxjs/operators";
|
|
11
10
|
|
|
12
|
-
/**
|
|
13
|
-
* Example interface for the configuration needed for long-press seeking.
|
|
14
|
-
*/
|
|
15
11
|
interface LongPressConfig {
|
|
16
|
-
|
|
17
|
-
TIMER_INTERVAL_MS: number; // ms
|
|
18
|
-
SKIP_INCREMENT_STEP: number; // seconds
|
|
19
|
-
MAX_SKIP_PER_TICK: number; // seconds
|
|
20
|
-
INITIAL_SKIP_AMOUNT: number; // e.g., 10s to start
|
|
12
|
+
TIMER_INTERVAL_MS: number;
|
|
21
13
|
}
|
|
22
14
|
|
|
23
15
|
export class LongPressSeekController implements ISeekController {
|
|
24
|
-
private pressStartTime = 0;
|
|
25
|
-
private isLongPressActive = false;
|
|
26
|
-
private currentSkipAmount = 0;
|
|
27
16
|
private subscription: Subscription | null = null;
|
|
28
|
-
|
|
17
|
+
readonly title = "LongPressSeek";
|
|
29
18
|
constructor(
|
|
30
|
-
private
|
|
19
|
+
private seekControllerDelegate: ISeekControllerDelegate,
|
|
20
|
+
private seekIncrementalStrategy: SeekIncrementalBaseStrategy,
|
|
31
21
|
private config: LongPressConfig
|
|
32
22
|
) {}
|
|
33
23
|
|
|
34
24
|
onStartSeek(seekType: SEEK_TYPE) {
|
|
35
25
|
this.cleanUpSubscription();
|
|
36
26
|
|
|
37
|
-
log_debug(
|
|
38
|
-
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
this.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
this.
|
|
62
|
-
|
|
63
|
-
);
|
|
64
|
-
})
|
|
65
|
-
).subscribe({
|
|
66
|
-
complete: () =>
|
|
67
|
-
log_verbose("LongPressSeekController: Long press subscription complete"),
|
|
68
|
-
error: (err) =>
|
|
69
|
-
log_error("LongPressSeekController: Long press subscription error", err),
|
|
70
|
-
});
|
|
27
|
+
log_debug(`${this.title}: start long press seek type: ${seekType}.`);
|
|
28
|
+
|
|
29
|
+
this.seekControllerDelegate.handlePlayerPause();
|
|
30
|
+
|
|
31
|
+
this.subscription = interval(this.config.TIMER_INTERVAL_MS)
|
|
32
|
+
.pipe(
|
|
33
|
+
tap(() => {
|
|
34
|
+
this.seekControllerDelegate.handleDelayedSeek(
|
|
35
|
+
this.seekIncrementalStrategy.getSkipTime()
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
log_debug(
|
|
39
|
+
`${
|
|
40
|
+
this.title
|
|
41
|
+
}: handle delayed seek type: ${seekType} - multiplier: ${this.seekIncrementalStrategy.getSkipMultiplier()}, currentSkipAmount: ${this.seekIncrementalStrategy.getSkipTime()}.`
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
this.seekIncrementalStrategy.increment();
|
|
45
|
+
})
|
|
46
|
+
)
|
|
47
|
+
.subscribe({
|
|
48
|
+
complete: () =>
|
|
49
|
+
log_verbose(`${this.title}: Long press subscription complete`),
|
|
50
|
+
error: (err) =>
|
|
51
|
+
log_error(`${this.title}: Long press subscription error`, err),
|
|
52
|
+
});
|
|
71
53
|
}
|
|
72
54
|
|
|
73
55
|
onSeekEnd() {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (
|
|
77
|
-
this.isLongPressActive &&
|
|
78
|
-
pressDuration > this.config.LONG_PRESS_THRESHOLD
|
|
79
|
-
) {
|
|
80
|
-
const offset = this.parent.delayedSeekOffset;
|
|
81
|
-
|
|
82
|
-
if (offset) {
|
|
83
|
-
log_debug(
|
|
84
|
-
`LongPressSeekController: onPressEnd for LongPressSeekController, seek offset: ${offset}`
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
this.parent.seekPlayer(offset);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
this.parent.resetAll();
|
|
56
|
+
this.cleanUpSubscription();
|
|
57
|
+
this.seekControllerDelegate.handleSeekComplete();
|
|
92
58
|
}
|
|
93
59
|
|
|
94
60
|
reset() {
|
|
95
|
-
this.
|
|
96
|
-
this.currentSkipAmount = 0;
|
|
61
|
+
this.seekIncrementalStrategy.reset();
|
|
97
62
|
this.cleanUpSubscription();
|
|
98
63
|
}
|
|
99
64
|
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { BehaviorSubject } from "rxjs";
|
|
2
2
|
import { TapSeekController } from "./TapSeekController";
|
|
3
3
|
import { LongPressSeekController } from "./LongPressSeekController";
|
|
4
|
-
import { playerManager } from "../../../appUtils";
|
|
5
4
|
import { SEEK_TYPE } from "../const";
|
|
6
5
|
import { createLogger } from "../../../logger";
|
|
7
6
|
import { Player } from "../../../appUtils/playerManager/player";
|
|
8
7
|
import { SeekKeyHandlerListener } from "../../../appUtils/keyInputHandler/KeyInputHandler";
|
|
9
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
ISeekController,
|
|
10
|
+
ISeekControllerDelegate,
|
|
11
|
+
SeekIncrementalBaseStrategy,
|
|
12
|
+
} from "./ISeekController";
|
|
10
13
|
|
|
11
14
|
export const { log_warning, log_debug, log_info, log_error, log_verbose } =
|
|
12
15
|
createLogger({
|
|
@@ -19,70 +22,87 @@ export enum SEEK_STRATEGY {
|
|
|
19
22
|
AUTO_SEEK = "AUTO_SEEK",
|
|
20
23
|
}
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
const CONFIG_TAP_SEEK = {
|
|
26
|
+
CLICKS_BEFORE_PAUSE: 0,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const CONFIG_LONG_PRESS = {
|
|
30
|
+
TIMER_INTERVAL_MS: 200,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const LONG_PRESS_THRESHOLD = 500;
|
|
34
|
+
const SEEK_END_TIMEOUT_DURATION = 700;
|
|
35
|
+
// prettier-ignore
|
|
36
|
+
|
|
37
|
+
export class TVSeekController
|
|
38
|
+
implements SeekKeyHandlerListener, ISeekControllerDelegate {
|
|
39
|
+
public delayedSeekPosition: number | null = null;
|
|
40
|
+
private readonly playerController: Player;
|
|
27
41
|
private delayedSeekProgressSubject = new BehaviorSubject<number | null>(null);
|
|
42
|
+
isPlayerInitiallyPaused: null | boolean = null;
|
|
43
|
+
currentSeekType: SEEK_TYPE | null = null;
|
|
28
44
|
|
|
29
45
|
private currentSeekController: ISeekController;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
private tapSeekController: TapSeekController;
|
|
33
|
-
private longPressSeekController: LongPressSeekController;
|
|
46
|
+
private readonly tapSeekController: TapSeekController;
|
|
47
|
+
private readonly longPressSeekController: LongPressSeekController;
|
|
34
48
|
private longPressThresholdTimeout: any | null = null;
|
|
49
|
+
private seekEndTimeout: any | null = null;
|
|
35
50
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
LONG_PRESS_THRESHOLD: 500,
|
|
39
|
-
TIMER_INTERVAL_MS: 300,
|
|
40
|
-
SKIP_INCREMENT_STEP: 5,
|
|
41
|
-
MAX_SKIP_PER_TICK: 300,
|
|
42
|
-
INITIAL_SKIP_AMOUNT: 10,
|
|
43
|
-
};
|
|
51
|
+
constructor(playerController: Player) {
|
|
52
|
+
this.playerController = playerController;
|
|
44
53
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
this.tapSeekController = new TapSeekController(
|
|
55
|
+
this,
|
|
56
|
+
new SeekIncrementalBaseStrategy(),
|
|
57
|
+
CONFIG_TAP_SEEK
|
|
58
|
+
);
|
|
48
59
|
|
|
49
60
|
this.longPressSeekController = new LongPressSeekController(
|
|
50
61
|
this,
|
|
51
|
-
|
|
62
|
+
new SeekIncrementalBaseStrategy(),
|
|
63
|
+
CONFIG_LONG_PRESS
|
|
52
64
|
);
|
|
65
|
+
|
|
66
|
+
this.currentSeekController = this.tapSeekController;
|
|
53
67
|
}
|
|
54
68
|
|
|
55
69
|
getDelayedSeekProgressObservable = () => {
|
|
56
70
|
return this.delayedSeekProgressSubject;
|
|
57
|
-
}
|
|
71
|
+
};
|
|
58
72
|
|
|
59
73
|
setStrategy = (seekStrategy: SEEK_STRATEGY) => {
|
|
60
74
|
switch (seekStrategy) {
|
|
61
75
|
case SEEK_STRATEGY.TAP_SEEK:
|
|
62
|
-
clearTimeout(this.longPressThresholdTimeout);
|
|
63
|
-
this.longPressThresholdTimeout = null;
|
|
64
76
|
this.currentSeekController = this.tapSeekController;
|
|
77
|
+
log_debug("TVSeekController: setStrategy - TAP_SEEK");
|
|
65
78
|
|
|
66
79
|
break;
|
|
67
80
|
case SEEK_STRATEGY.AUTO_SEEK:
|
|
68
|
-
this.currentSeekController.reset();
|
|
69
81
|
this.currentSeekController = this.longPressSeekController;
|
|
82
|
+
log_debug("TVSeekController: setStrategy - AUTO_SEEK");
|
|
83
|
+
|
|
70
84
|
break;
|
|
71
85
|
default:
|
|
72
86
|
}
|
|
73
|
-
}
|
|
87
|
+
};
|
|
74
88
|
|
|
75
89
|
handleStartSeek = (seekType: SEEK_TYPE) => {
|
|
90
|
+
this.currentSeekType = seekType;
|
|
91
|
+
|
|
92
|
+
if (this.isPlayerInitiallyPaused === null) {
|
|
93
|
+
this.isPlayerInitiallyPaused = this.playerController.isPaused();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
this.clearLongPressThresholdTimeout();
|
|
97
|
+
this.clearSeekCompleteTimeout();
|
|
76
98
|
this.setStrategy(SEEK_STRATEGY.TAP_SEEK);
|
|
77
99
|
this.currentSeekController.onStartSeek(seekType);
|
|
78
|
-
log_verbose(`handleStartSeek: TAP seekType: ${seekType}`);
|
|
79
100
|
|
|
80
101
|
this.longPressThresholdTimeout = setTimeout(() => {
|
|
81
|
-
|
|
82
|
-
this.setStrategy(SEEK_STRATEGY.AUTO_SEEK);
|
|
102
|
+
this.setStrategy(SEEK_STRATEGY.AUTO_SEEK);
|
|
83
103
|
this.currentSeekController.onStartSeek(seekType);
|
|
84
|
-
},
|
|
85
|
-
}
|
|
104
|
+
}, LONG_PRESS_THRESHOLD);
|
|
105
|
+
};
|
|
86
106
|
|
|
87
107
|
onForwardPress = (): boolean => {
|
|
88
108
|
this.handleStartSeek(SEEK_TYPE.FORWARD);
|
|
@@ -97,103 +117,142 @@ export class TVSeekController implements SeekKeyHandlerListener {
|
|
|
97
117
|
};
|
|
98
118
|
|
|
99
119
|
onSeekEnd = () => {
|
|
120
|
+
this.clearLongPressThresholdTimeout();
|
|
100
121
|
this.currentSeekController.onSeekEnd();
|
|
101
|
-
|
|
102
|
-
}
|
|
122
|
+
};
|
|
103
123
|
|
|
104
124
|
onRewindPressOut = () => {
|
|
105
|
-
const isAutoSeek = this.currentSeekController === this.longPressSeekController;
|
|
106
|
-
|
|
107
|
-
log_verbose(`handleStartSeek: onRewindPressOut, isAutoSeek: ${isAutoSeek}`);
|
|
108
|
-
|
|
109
125
|
this.onSeekEnd();
|
|
110
|
-
}
|
|
126
|
+
};
|
|
111
127
|
|
|
112
128
|
onForwardPressOut = () => {
|
|
113
|
-
const isAutoSeek = this.currentSeekController === this.longPressSeekController;
|
|
114
|
-
|
|
115
|
-
log_verbose(`handleStartSeek: onForwardPressOut, isAutoSeek: ${isAutoSeek}`);
|
|
116
129
|
this.onSeekEnd();
|
|
117
|
-
}
|
|
130
|
+
};
|
|
118
131
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
132
|
+
onVideoProgress = (position: number) => {
|
|
133
|
+
if (this.delayedSeekPosition !== null) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
122
136
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const player = this.getPlayer();
|
|
126
|
-
if (!player) return;
|
|
137
|
+
this.delayedSeekProgressSubject.next(position);
|
|
138
|
+
};
|
|
127
139
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
140
|
+
handleDelayedSeek = (offset: number = 0) => {
|
|
141
|
+
this.handlePlayerPause();
|
|
142
|
+
|
|
143
|
+
const currentPos =
|
|
144
|
+
this.delayedSeekPosition === null
|
|
145
|
+
? this.playerController.getPosition()
|
|
146
|
+
: this.delayedSeekPosition;
|
|
132
147
|
|
|
133
|
-
const newOffset = (this.delayedSeekOffset || 0) + skipTime;
|
|
134
|
-
const currentPos = player.getPosition();
|
|
135
148
|
let targetPos = currentPos;
|
|
136
149
|
|
|
137
|
-
if (
|
|
138
|
-
targetPos = Math.min(
|
|
150
|
+
if (this.currentSeekType === SEEK_TYPE.FORWARD) {
|
|
151
|
+
targetPos = Math.min(
|
|
152
|
+
currentPos + offset,
|
|
153
|
+
this.playerController.getSeekableDuration()
|
|
154
|
+
);
|
|
155
|
+
} else if (this.currentSeekType === SEEK_TYPE.REWIND) {
|
|
156
|
+
targetPos = Math.max(0, currentPos - offset);
|
|
139
157
|
} else {
|
|
140
|
-
|
|
158
|
+
log_warning(
|
|
159
|
+
`TVSeekController: handleDelayedSeek - invalid seek type: ${this.currentSeekType}`
|
|
160
|
+
);
|
|
141
161
|
}
|
|
142
162
|
|
|
143
|
-
this.delayedSeekOffset = newOffset;
|
|
144
|
-
|
|
145
163
|
log_debug(
|
|
146
|
-
`TVSeekController: handleDelayedSeek - targetPos: ${targetPos},
|
|
164
|
+
`TVSeekController: handleDelayedSeek - targetPos: ${targetPos}, skipTime: ${offset}`
|
|
147
165
|
);
|
|
148
166
|
|
|
167
|
+
const prevValue = this.delayedSeekProgressSubject.getValue();
|
|
168
|
+
|
|
169
|
+
if (prevValue !== null) {
|
|
170
|
+
const delta = Math.abs(prevValue - targetPos);
|
|
171
|
+
|
|
172
|
+
if (delta < 1) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
this.delayedSeekPosition = targetPos;
|
|
149
178
|
this.delayedSeekProgressSubject.next(targetPos);
|
|
150
|
-
}
|
|
179
|
+
};
|
|
151
180
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (!player || !this.currentSeekType) return;
|
|
155
|
-
player[this.currentSeekType](offset);
|
|
156
|
-
this.playerPlay();
|
|
157
|
-
}
|
|
181
|
+
handleSeekTo = (newPosition: number) => {
|
|
182
|
+
this.playerController.seekTo(newPosition);
|
|
158
183
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
184
|
+
if (!this.isPlayerInitiallyPaused) {
|
|
185
|
+
this.playerController.play();
|
|
186
|
+
}
|
|
187
|
+
};
|
|
162
188
|
|
|
163
|
-
|
|
164
|
-
|
|
189
|
+
handleSeek = (offset: number) => {
|
|
190
|
+
this.playerController[this.currentSeekType](offset);
|
|
191
|
+
|
|
192
|
+
if (!this.isPlayerInitiallyPaused) {
|
|
193
|
+
this.playerController.play();
|
|
165
194
|
}
|
|
166
|
-
}
|
|
195
|
+
};
|
|
167
196
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
197
|
+
handleSeekComplete = () => {
|
|
198
|
+
this.clearSeekCompleteTimeout();
|
|
199
|
+
|
|
200
|
+
this.seekEndTimeout = setTimeout(() => {
|
|
201
|
+
if (this.delayedSeekPosition !== null) {
|
|
202
|
+
this.handleSeekTo(this.delayedSeekPosition);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
log_debug(
|
|
206
|
+
`handleSeekComplete: Seek finished, type: ${this.currentSeekController.title}, seekPosition: ${this.delayedSeekPosition}. Reset all states and seek position.`
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
this.resetAll();
|
|
210
|
+
}, SEEK_END_TIMEOUT_DURATION);
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
isSeeking = (): boolean => {
|
|
214
|
+
return this.delayedSeekPosition !== null;
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
handlePlayerPause = () => {
|
|
218
|
+
if (this.playerController.isPaused()) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
this.playerController.pause();
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
clearSeekCompleteTimeout = () => {
|
|
226
|
+
if (this.seekEndTimeout === null) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
clearTimeout(this.seekEndTimeout);
|
|
231
|
+
this.seekEndTimeout = null;
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
clearLongPressThresholdTimeout = () => {
|
|
235
|
+
if (this.longPressThresholdTimeout === null) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
clearTimeout(this.longPressThresholdTimeout);
|
|
240
|
+
this.longPressThresholdTimeout = null;
|
|
241
|
+
};
|
|
173
242
|
|
|
174
|
-
/**
|
|
175
|
-
* Reset *all* relevant state and strategy controllers
|
|
176
|
-
*/
|
|
177
243
|
resetAll() {
|
|
178
244
|
log_debug("TVSeekController: Reset all states");
|
|
179
245
|
this.currentSeekType = null;
|
|
180
|
-
this.delayedSeekOffset = null;
|
|
181
246
|
|
|
182
|
-
this.
|
|
247
|
+
this.delayedSeekPosition = null;
|
|
248
|
+
this.isPlayerInitiallyPaused = null;
|
|
249
|
+
|
|
250
|
+
this.clearSeekCompleteTimeout();
|
|
251
|
+
this.clearLongPressThresholdTimeout();
|
|
183
252
|
|
|
184
253
|
this.tapSeekController.reset();
|
|
185
254
|
this.longPressSeekController.reset();
|
|
186
255
|
|
|
187
256
|
this.setStrategy(SEEK_STRATEGY.TAP_SEEK);
|
|
188
257
|
}
|
|
189
|
-
|
|
190
|
-
private getPlayer() {
|
|
191
|
-
const player = playerManager.getInstanceController();
|
|
192
|
-
|
|
193
|
-
if (!player) {
|
|
194
|
-
log_warning("TVSeekController: No valid player controller found");
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return player;
|
|
198
|
-
}
|
|
199
258
|
}
|
|
@@ -1,75 +1,53 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import {
|
|
2
|
+
ISeekController,
|
|
3
|
+
ISeekControllerDelegate,
|
|
4
|
+
SeekIncrementalBaseStrategy,
|
|
5
|
+
} from "./ISeekController";
|
|
6
|
+
import { SEEK_TYPE } from "../const";
|
|
7
|
+
import { log_debug } from "./TVSeekController";
|
|
8
|
+
|
|
9
|
+
interface TapPressConfig {
|
|
10
|
+
CLICKS_BEFORE_PAUSE: number;
|
|
11
|
+
}
|
|
7
12
|
|
|
8
13
|
export class TapSeekController implements ISeekController {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
readonly title = "TapSeek";
|
|
15
|
+
constructor(
|
|
16
|
+
private seekControllerDelegate: ISeekControllerDelegate,
|
|
17
|
+
private seekIncrementalStrategy: SeekIncrementalBaseStrategy,
|
|
18
|
+
private config: TapPressConfig
|
|
19
|
+
) {}
|
|
14
20
|
|
|
15
21
|
onStartSeek = (seekType: SEEK_TYPE) => {
|
|
16
|
-
log_debug(
|
|
17
|
-
|
|
18
|
-
const prevSeekType = this.parent.currentSeekType;
|
|
19
|
-
|
|
20
|
-
if (prevSeekType !== seekType) {
|
|
21
|
-
this.reset();
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
this.parent.currentSeekType = seekType;
|
|
25
|
-
|
|
26
|
-
if (this.tapSequenceTimeout) {
|
|
27
|
-
clearTimeout(this.tapSequenceTimeout);
|
|
28
|
-
this.tapSequenceTimeout = null;
|
|
29
|
-
}
|
|
22
|
+
log_debug(`${this.title}: start tap seek type: ${seekType}.`);
|
|
30
23
|
|
|
31
|
-
const
|
|
32
|
-
const
|
|
24
|
+
const skipTime = this.seekIncrementalStrategy.getSkipTime();
|
|
25
|
+
const seekMultiplier = this.seekIncrementalStrategy.getSkipMultiplier();
|
|
33
26
|
|
|
34
|
-
if (
|
|
27
|
+
if (seekMultiplier > this.config.CLICKS_BEFORE_PAUSE) {
|
|
35
28
|
log_debug(
|
|
36
|
-
|
|
29
|
+
`${this.title}: handle delayed seek type:${seekType} - skipTime: ${skipTime}, seekMultiplier: ${seekMultiplier}.`
|
|
37
30
|
);
|
|
38
31
|
|
|
39
|
-
this.
|
|
32
|
+
this.seekControllerDelegate.handleDelayedSeek(skipTime);
|
|
40
33
|
} else {
|
|
41
34
|
log_debug(
|
|
42
|
-
|
|
35
|
+
`${this.title}: handle seek type:${seekType} - skipTime: ${skipTime}.`
|
|
43
36
|
);
|
|
44
37
|
|
|
45
|
-
this.
|
|
38
|
+
this.seekControllerDelegate.handleSeek(skipTime);
|
|
46
39
|
}
|
|
47
40
|
|
|
48
|
-
this.
|
|
49
|
-
}
|
|
41
|
+
this.seekIncrementalStrategy.increment();
|
|
42
|
+
};
|
|
50
43
|
|
|
51
44
|
onSeekEnd = () => {
|
|
52
|
-
this.
|
|
53
|
-
if (this.parent.delayedSeekOffset !== null) {
|
|
54
|
-
this.parent.seekPlayer(this.parent.delayedSeekOffset);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
log_debug(
|
|
58
|
-
`TapSeekController: Tap sequence timeout fired, delayedSeekOffset: ${this.parent.delayedSeekOffset}.`
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
this.parent.resetAll();
|
|
62
|
-
}, this.ON_PRESS_RESET_DURATION);
|
|
45
|
+
this.seekControllerDelegate.handleSeekComplete();
|
|
63
46
|
};
|
|
64
47
|
|
|
65
48
|
reset = () => {
|
|
66
|
-
log_debug("
|
|
67
|
-
|
|
68
|
-
this.seekMultiplier = 1;
|
|
49
|
+
log_debug("{this.title}: Reset states");
|
|
69
50
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
this.tapSequenceTimeout = null;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
51
|
+
this.seekIncrementalStrategy.reset();
|
|
52
|
+
};
|
|
75
53
|
}
|