@applicaster/zapp-react-native-utils 13.0.0-alpha.4515349201 → 13.0.0-alpha.4750811552

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.
@@ -78,9 +78,9 @@ const web = {
78
78
  Escape: { code: "Escape", keyCode: 27 },
79
79
  EscapeDevice: { code: "Escape", keyCode: 27 },
80
80
  // For Debugging
81
- Z: { code: "KeyZ", keyCode: 90 },
82
- X: { code: "KeyX", keyCode: 88 },
83
- C: { code: "KeyC", keyCode: 67 },
81
+ Z: { code: "KeyZ", keyCode: 90 }, // Hardware Backward Button
82
+ X: { code: "KeyX", keyCode: 88 }, // Hardware Enter Button
83
+ C: { code: "KeyC", keyCode: 67 }, // Hardware Forward Button
84
84
  };
85
85
 
86
86
  const android_tv = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-native-utils",
3
- "version": "13.0.0-alpha.4515349201",
3
+ "version": "13.0.0-alpha.4750811552",
4
4
  "description": "Applicaster Zapp React Native utilities package",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -28,7 +28,7 @@
28
28
  },
29
29
  "homepage": "https://github.com/applicaster/quickbrick#readme",
30
30
  "dependencies": {
31
- "@applicaster/applicaster-types": "13.0.0-alpha.4515349201",
31
+ "@applicaster/applicaster-types": "13.0.0-alpha.4750811552",
32
32
  "buffer": "^5.2.1",
33
33
  "camelize": "^1.0.0",
34
34
  "create-react-context": "^0.2.3",
@@ -1,15 +1,32 @@
1
- import { SEEK_TYPE } from "../const";
1
+ import { MAX_SEEK_TIME, SEEK_TYPE, SKIP_TIME_BASE } from "../const";
2
2
 
3
3
  export interface ISeekControllerDelegate {
4
4
  handleDelayedSeek(offset: number): void;
5
5
  handleSeek(offset: number): void;
6
6
  handleSeekComplete(): void;
7
7
  handlePlayerPause(): void;
8
- delayedSeekOffset: number | null;
9
8
  }
10
9
 
11
10
  export interface ISeekController {
11
+ title: string;
12
12
  onStartSeek(seekType: SEEK_TYPE): void;
13
13
  onSeekEnd(): void;
14
14
  reset(): void;
15
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,85 +1,67 @@
1
- import { ISeekController, ISeekControllerDelegate } from "./ISeekController";
1
+ import {
2
+ ISeekController,
3
+ ISeekControllerDelegate,
4
+ SeekIncrementalBaseStrategy,
5
+ } from "./ISeekController";
2
6
  import { SEEK_TYPE } from "../const";
3
7
  import { log_debug, log_error, log_verbose } from "./TVSeekController";
4
8
  import { interval, Subscription } from "rxjs";
5
9
  import { tap } from "rxjs/operators";
6
10
 
7
11
  interface LongPressConfig {
8
- TIMER_INTERVAL_MS: number; // ms
9
- SKIP_INCREMENT_STEP: number; // seconds
10
- MAX_SKIP_PER_TICK: number; // seconds
11
- INITIAL_SKIP_AMOUNT: number; // e.g., 10s to start
12
+ TIMER_INTERVAL_MS: number;
12
13
  }
13
14
 
14
15
  export class LongPressSeekController implements ISeekController {
15
16
  isLongPressActive = false;
16
- private currentSkipAmount = 0;
17
17
  private subscription: Subscription | null = null;
18
-
18
+ readonly title = "LongPressSeek";
19
19
  constructor(
20
20
  private seekControllerDelegate: ISeekControllerDelegate,
21
+ private seekIncrementalStrategy: SeekIncrementalBaseStrategy,
21
22
  private config: LongPressConfig
22
23
  ) {}
23
24
 
24
25
  onStartSeek(seekType: SEEK_TYPE) {
25
26
  this.cleanUpSubscription();
26
27
 
27
- log_debug(
28
- `LongPressSeekController: start long press seek type:${seekType}.`
29
- );
28
+ log_debug(`${this.title}: start long press seek type: ${seekType}.`);
30
29
 
31
30
  this.seekControllerDelegate.handlePlayerPause();
32
31
 
33
32
  this.isLongPressActive = true;
34
- this.currentSkipAmount = this.config.INITIAL_SKIP_AMOUNT;
35
33
 
36
34
  this.subscription = interval(this.config.TIMER_INTERVAL_MS)
37
35
  .pipe(
38
36
  tap(() => {
39
- this.seekControllerDelegate.handleDelayedSeek(this.currentSkipAmount);
37
+ this.seekControllerDelegate.handleDelayedSeek(
38
+ this.seekIncrementalStrategy.getSkipTime()
39
+ );
40
40
 
41
41
  log_debug(
42
- `LongPressSeekController: handle delayed seek type: ${seekType} - currentSkipAmount: ${this.currentSkipAmount}.`
42
+ `${
43
+ this.title
44
+ }: handle delayed seek type: ${seekType} - multiplier: ${this.seekIncrementalStrategy.getSkipMultiplier()}, currentSkipAmount: ${this.seekIncrementalStrategy.getSkipTime()}.`
43
45
  );
44
46
 
45
- this.currentSkipAmount = Math.min(
46
- this.currentSkipAmount + this.config.SKIP_INCREMENT_STEP,
47
- this.config.MAX_SKIP_PER_TICK
48
- );
47
+ this.seekIncrementalStrategy.increment();
49
48
  })
50
49
  )
51
50
  .subscribe({
52
51
  complete: () =>
53
- log_verbose(
54
- "LongPressSeekController: Long press subscription complete"
55
- ),
52
+ log_verbose(`${this.title}: Long press subscription complete`),
56
53
  error: (err) =>
57
- log_error(
58
- "LongPressSeekController: Long press subscription error",
59
- err
60
- ),
54
+ log_error(`${this.title}: Long press subscription error`, err),
61
55
  });
62
56
  }
63
57
 
64
58
  onSeekEnd() {
65
- if (this.isLongPressActive) {
66
- const offset = this.seekControllerDelegate.delayedSeekOffset;
67
-
68
- if (offset !== null) {
69
- log_debug(
70
- `LongPressSeekController: onPressEnd for LongPressSeekController, seek offset: ${offset}`
71
- );
72
-
73
- this.seekControllerDelegate.handleSeek(offset);
74
- }
75
- }
76
-
77
59
  this.seekControllerDelegate.handleSeekComplete();
78
60
  }
79
61
 
80
62
  reset() {
81
63
  this.isLongPressActive = false;
82
- this.currentSkipAmount = 0;
64
+ this.seekIncrementalStrategy.reset();
83
65
  this.cleanUpSubscription();
84
66
  }
85
67
 
@@ -5,7 +5,11 @@ import { SEEK_TYPE } from "../const";
5
5
  import { createLogger } from "../../../logger";
6
6
  import { Player } from "../../../appUtils/playerManager/player";
7
7
  import { SeekKeyHandlerListener } from "../../../appUtils/keyInputHandler/KeyInputHandler";
8
- import { ISeekController, ISeekControllerDelegate } from "./ISeekController";
8
+ import {
9
+ ISeekController,
10
+ ISeekControllerDelegate,
11
+ SeekIncrementalBaseStrategy,
12
+ } from "./ISeekController";
9
13
 
10
14
  export const { log_warning, log_debug, log_info, log_error, log_verbose } =
11
15
  createLogger({
@@ -23,17 +27,16 @@ const CONFIG_TAP_SEEK = {
23
27
  };
24
28
 
25
29
  const CONFIG_LONG_PRESS = {
26
- TIMER_INTERVAL_MS: 300,
27
- SKIP_INCREMENT_STEP: 5,
28
- MAX_SKIP_PER_TICK: 300,
29
- INITIAL_SKIP_AMOUNT: 10,
30
+ TIMER_INTERVAL_MS: 200,
30
31
  };
31
32
 
32
33
  const LONG_PRESS_THRESHOLD = 500;
34
+ const SEEK_END_TIMEOUT_DURATION = 700;
33
35
 
34
36
  export class TVSeekController
35
- implements SeekKeyHandlerListener, ISeekControllerDelegate {
36
- public delayedSeekOffset: number | null = null;
37
+ implements SeekKeyHandlerListener, ISeekControllerDelegate
38
+ {
39
+ public delayedSeekPosition: number | null = null;
37
40
  private readonly playerController: Player;
38
41
  private delayedSeekProgressSubject = new BehaviorSubject<number | null>(null);
39
42
  isPlayerInitiallyPaused: null | boolean = null;
@@ -43,14 +46,20 @@ export class TVSeekController
43
46
  private readonly tapSeekController: TapSeekController;
44
47
  private readonly longPressSeekController: LongPressSeekController;
45
48
  private longPressThresholdTimeout: any | null = null;
49
+ private seekEndTimeout: any | null = null;
46
50
 
47
51
  constructor(playerController: Player) {
48
52
  this.playerController = playerController;
49
53
 
50
- this.tapSeekController = new TapSeekController(this, CONFIG_TAP_SEEK);
54
+ this.tapSeekController = new TapSeekController(
55
+ this,
56
+ new SeekIncrementalBaseStrategy(),
57
+ CONFIG_TAP_SEEK
58
+ );
51
59
 
52
60
  this.longPressSeekController = new LongPressSeekController(
53
61
  this,
62
+ new SeekIncrementalBaseStrategy(),
54
63
  CONFIG_LONG_PRESS
55
64
  );
56
65
 
@@ -64,30 +73,28 @@ export class TVSeekController
64
73
  setStrategy = (seekStrategy: SEEK_STRATEGY) => {
65
74
  switch (seekStrategy) {
66
75
  case SEEK_STRATEGY.TAP_SEEK:
67
- clearTimeout(this.longPressThresholdTimeout);
68
- this.longPressThresholdTimeout = null;
69
76
  this.currentSeekController = this.tapSeekController;
77
+ log_debug("TVSeekController: setStrategy - TAP_SEEK");
70
78
 
71
79
  break;
72
80
  case SEEK_STRATEGY.AUTO_SEEK:
73
- this.currentSeekController.reset();
74
81
  this.currentSeekController = this.longPressSeekController;
82
+ log_debug("TVSeekController: setStrategy - AUTO_SEEK");
83
+
75
84
  break;
76
85
  default:
77
86
  }
78
87
  };
79
88
 
80
89
  handleStartSeek = (seekType: SEEK_TYPE) => {
81
- if (this.currentSeekType !== seekType) {
82
- this.resetAll();
83
- }
84
-
85
90
  this.currentSeekType = seekType;
86
91
 
87
92
  if (this.isPlayerInitiallyPaused === null) {
88
93
  this.isPlayerInitiallyPaused = this.playerController.isPaused();
89
94
  }
90
95
 
96
+ this.clearLongPressThresholdTimeout();
97
+ this.clearSeekCompleteTimeout();
91
98
  this.setStrategy(SEEK_STRATEGY.TAP_SEEK);
92
99
  this.currentSeekController.onStartSeek(seekType);
93
100
 
@@ -123,7 +130,7 @@ export class TVSeekController
123
130
  };
124
131
 
125
132
  public onVideoProgress = (position: number) => {
126
- if (this.delayedSeekOffset) {
133
+ if (this.delayedSeekPosition) {
127
134
  return;
128
135
  }
129
136
 
@@ -132,30 +139,29 @@ export class TVSeekController
132
139
  this.delayedSeekProgressSubject.next(position);
133
140
  };
134
141
 
135
- handleDelayedSeek = (offset: number) => {
142
+ handleDelayedSeek = (offset: number = 0) => {
136
143
  this.handlePlayerPause();
137
144
 
138
- const newOffset = (this.delayedSeekOffset || 0) + offset;
139
- const currentPos = this.playerController.getPosition();
145
+ const currentPos =
146
+ this.delayedSeekPosition || this.playerController.getPosition();
147
+
140
148
  let targetPos = currentPos;
141
149
 
142
150
  if (this.currentSeekType === SEEK_TYPE.FORWARD) {
143
151
  targetPos = Math.min(
144
- currentPos + newOffset,
152
+ currentPos + offset,
145
153
  this.playerController.getSeekableDuration()
146
154
  );
147
155
  } else if (this.currentSeekType === SEEK_TYPE.REWIND) {
148
- targetPos = Math.max(0, currentPos - newOffset);
156
+ targetPos = Math.max(0, currentPos - offset);
149
157
  } else {
150
158
  log_warning(
151
159
  `TVSeekController: handleDelayedSeek - invalid seek type: ${this.currentSeekType}`
152
160
  );
153
161
  }
154
162
 
155
- this.delayedSeekOffset = newOffset;
156
-
157
163
  log_debug(
158
- `TVSeekController: handleDelayedSeek - targetPos: ${targetPos}, offset: ${newOffset}, skipTime: ${offset}`
164
+ `TVSeekController: handleDelayedSeek - targetPos: ${targetPos}, skipTime: ${offset}`
159
165
  );
160
166
 
161
167
  const prevValue = this.delayedSeekProgressSubject.getValue();
@@ -168,9 +174,18 @@ export class TVSeekController
168
174
  }
169
175
  }
170
176
 
177
+ this.delayedSeekPosition = targetPos;
171
178
  this.delayedSeekProgressSubject.next(targetPos);
172
179
  };
173
180
 
181
+ handleSeekTo = (newPosition: number) => {
182
+ this.playerController.seekTo(newPosition);
183
+
184
+ if (!this.isPlayerInitiallyPaused) {
185
+ this.playerController.play();
186
+ }
187
+ };
188
+
174
189
  handleSeek = (offset: number) => {
175
190
  this.playerController[this.currentSeekType](offset);
176
191
 
@@ -180,7 +195,23 @@ export class TVSeekController
180
195
  };
181
196
 
182
197
  handleSeekComplete = () => {
183
- this.resetAll();
198
+ this.clearSeekCompleteTimeout();
199
+
200
+ this.seekEndTimeout = setTimeout(() => {
201
+ if (this.delayedSeekPosition !== null) {
202
+ // const offset = Math.abs(
203
+ // this.playerController.getPosition() - this.delayedSeekPosition
204
+ // );
205
+
206
+ this.handleSeekTo(this.delayedSeekPosition);
207
+ }
208
+
209
+ log_debug(
210
+ `handleSeekComplete: Seek finished, type: ${this.currentSeekController.title}, seekPosition: ${this.delayedSeekPosition}. Reset all states and seek position.`
211
+ );
212
+
213
+ this.resetAll();
214
+ }, SEEK_END_TIMEOUT_DURATION);
184
215
  };
185
216
 
186
217
  isAutoSeeking = (): boolean => {
@@ -195,12 +226,34 @@ export class TVSeekController
195
226
  this.playerController.pause();
196
227
  };
197
228
 
229
+ clearSeekCompleteTimeout = () => {
230
+ if (this.seekEndTimeout === null) {
231
+ return;
232
+ }
233
+
234
+ clearTimeout(this.seekEndTimeout);
235
+ this.seekEndTimeout = null;
236
+ };
237
+
238
+ clearLongPressThresholdTimeout = () => {
239
+ if (this.longPressThresholdTimeout === null) {
240
+ return;
241
+ }
242
+
243
+ clearTimeout(this.longPressThresholdTimeout);
244
+ this.longPressThresholdTimeout = null;
245
+ };
246
+
198
247
  resetAll() {
199
248
  log_debug("TVSeekController: Reset all states");
200
249
  this.currentSeekType = null;
201
- this.delayedSeekOffset = null;
250
+
251
+ this.delayedSeekPosition = null;
202
252
  this.isPlayerInitiallyPaused = null;
203
253
 
254
+ this.clearSeekCompleteTimeout();
255
+ this.clearLongPressThresholdTimeout();
256
+
204
257
  this.tapSeekController.reset();
205
258
  this.longPressSeekController.reset();
206
259
 
@@ -1,75 +1,53 @@
1
- import { ISeekController, ISeekControllerDelegate } from "./ISeekController";
2
- import { SEEK_TYPE, MAX_SEEK_TIME, SKIP_TIME_BASE } from "../const";
1
+ import {
2
+ ISeekController,
3
+ ISeekControllerDelegate,
4
+ SeekIncrementalBaseStrategy,
5
+ } from "./ISeekController";
6
+ import { SEEK_TYPE } from "../const";
3
7
  import { log_debug } from "./TVSeekController";
4
8
 
5
9
  interface TapPressConfig {
6
10
  CLICKS_BEFORE_PAUSE: number;
7
11
  }
8
12
 
9
- const getSkipTime = (multiplier: number) =>
10
- Math.min(MAX_SEEK_TIME, SKIP_TIME_BASE * multiplier);
11
-
12
13
  export class TapSeekController implements ISeekController {
13
- private tapSequenceTimeout: any | null = null;
14
- private seekMultiplier = 1;
15
- private readonly ON_PRESS_RESET_DURATION = 500; // ms
16
-
14
+ readonly title = "TapSeek";
17
15
  constructor(
18
16
  private seekControllerDelegate: ISeekControllerDelegate,
17
+ private seekIncrementalStrategy: SeekIncrementalBaseStrategy,
19
18
  private config: TapPressConfig
20
19
  ) {}
21
20
 
22
21
  onStartSeek = (seekType: SEEK_TYPE) => {
23
- log_debug(`TapSeekController: start tap seek type: ${seekType}.`);
22
+ log_debug(`${this.title}: start tap seek type: ${seekType}.`);
24
23
 
25
- if (this.tapSequenceTimeout) {
26
- clearTimeout(this.tapSequenceTimeout);
27
- this.tapSequenceTimeout = null;
28
- }
24
+ const skipTime = this.seekIncrementalStrategy.getSkipTime();
25
+ const seekMultiplier = this.seekIncrementalStrategy.getSkipMultiplier();
29
26
 
30
- const skipTime = getSkipTime(this.seekMultiplier);
31
-
32
- if (this.seekMultiplier > this.config.CLICKS_BEFORE_PAUSE) {
27
+ if (seekMultiplier > this.config.CLICKS_BEFORE_PAUSE) {
33
28
  log_debug(
34
- `TapSeekController: handle delayed seek type:${seekType} - skipTime: ${skipTime}.`
29
+ `${this.title}: handle delayed seek type:${seekType} - skipTime: ${skipTime}, seekMultiplier: ${seekMultiplier}.`
35
30
  );
36
31
 
37
32
  this.seekControllerDelegate.handleDelayedSeek(skipTime);
38
33
  } else {
39
34
  log_debug(
40
- `TapSeekController: handle seek type:${seekType} - skipTime: ${skipTime}.`
35
+ `${this.title}: handle seek type:${seekType} - skipTime: ${skipTime}.`
41
36
  );
42
37
 
43
38
  this.seekControllerDelegate.handleSeek(skipTime);
44
39
  }
45
40
 
46
- this.seekMultiplier++;
41
+ this.seekIncrementalStrategy.increment();
47
42
  };
48
43
 
49
44
  onSeekEnd = () => {
50
- this.tapSequenceTimeout = setTimeout(() => {
51
- if (this.seekControllerDelegate.delayedSeekOffset !== null) {
52
- this.seekControllerDelegate.handleSeek(
53
- this.seekControllerDelegate.delayedSeekOffset
54
- );
55
- }
56
-
57
- log_debug(
58
- `TapSeekController: Tap sequence timeout fired, delayedSeekOffset: ${this.seekControllerDelegate.delayedSeekOffset}.`
59
- );
60
-
61
- this.seekControllerDelegate.handleSeekComplete();
62
- }, this.ON_PRESS_RESET_DURATION);
45
+ this.seekControllerDelegate.handleSeekComplete();
63
46
  };
64
47
 
65
48
  reset = () => {
66
- log_debug("TapSeekController: Reset states");
49
+ log_debug("{this.title}: Reset states");
67
50
 
68
- this.seekMultiplier = 1;
69
-
70
- if (this.tapSequenceTimeout) {
71
- clearTimeout(this.tapSequenceTimeout);
72
- this.tapSequenceTimeout = null;
73
- }
51
+ this.seekIncrementalStrategy.reset();
74
52
  };
75
53
  }
@@ -1,6 +1,6 @@
1
1
  export const SKIP_TIME_BASE = 10;
2
2
 
3
- export const MAX_SEEK_TIME = 60;
3
+ export const MAX_SEEK_TIME = 600;
4
4
 
5
5
  export enum SEEK_TYPE {
6
6
  FORWARD = "forward",