@24i/bigscreen-sdk 1.0.20 → 1.0.21-alpha.2358

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@24i/bigscreen-sdk",
3
- "version": "1.0.20",
3
+ "version": "1.0.21-alpha.2358",
4
4
  "description": "SmartApps BIGscreen SDK monorepo",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -41,7 +41,7 @@
41
41
  "@24i/appstage-shared-events-manager": "1.0.7",
42
42
  "@24i/appstage-shared-perf-utils": "1.0.5",
43
43
  "@24i/player-base": "6.10.0",
44
- "@24i/smartapps-datalayer": "3.0.8",
44
+ "@24i/smartapps-datalayer": "3.0.9-alpha.1010",
45
45
  "@sentry/browser": "7.35.0",
46
46
  "@sentry/types": "7.35.0",
47
47
  "@types/gtag.js": "^0.0.12",
@@ -121,6 +121,7 @@
121
121
  "./driver-hbbtv": "./packages/driver-hbbtv/src/index.ts",
122
122
  "./driver-kreatv": "./packages/driver-kreatv/src/index.ts",
123
123
  "./driver-saphi": "./packages/driver-saphi/src/index.ts",
124
+ "./driver-smartcast": "./packages/driver-smartcast/src/index.ts",
124
125
  "./driver-tizen": "./packages/driver-tizen/src/index.ts",
125
126
  "./driver-vidaa": "./packages/driver-vidaa/src/index.ts",
126
127
  "./driver-webos/scripts/template": "./packages/driver-webos/scripts/template.ts",
@@ -0,0 +1 @@
1
+ export { DeviceSmartCast as Device } from '@24i/bigscreen-sdk/driver-smartcast';
@@ -7,6 +7,7 @@ import { DeviceKreaTV } from '@24i/bigscreen-sdk/driver-kreatv';
7
7
  import { DeviceSaphi } from '@24i/bigscreen-sdk/driver-saphi';
8
8
  import { DeviceTizen } from '@24i/bigscreen-sdk/driver-tizen';
9
9
  import { DeviceVidaa } from '@24i/bigscreen-sdk/driver-vidaa';
10
+ import { DeviceSmartCast } from '@24i/bigscreen-sdk/driver-smartcast';
10
11
  import { DeviceWebos } from '@24i/bigscreen-sdk/driver-webos';
11
12
  import { DeviceXbox } from '@24i/bigscreen-sdk/driver-xbox';
12
13
 
@@ -29,6 +30,10 @@ const getDevice = () => {
29
30
  info('Tizen');
30
31
  return new DeviceTizen();
31
32
  }
33
+ if (userAgent.match(/Vizio/i)) {
34
+ info('SmartCast');
35
+ return new DeviceSmartCast();
36
+ }
32
37
  if (userAgent.match(/Web0S/)) { // Yes, it is Web ZERO S
33
38
  info('WebOS');
34
39
  return new DeviceWebos();
@@ -217,4 +217,8 @@ export abstract class DeviceBase<
217
217
  isMouseUsed() {
218
218
  return this.isMouseActive;
219
219
  }
220
+
221
+ getResolution() {
222
+ return { width: window.screen?.width, height: window.screen?.height };
223
+ }
220
224
  }
@@ -1,9 +1,11 @@
1
1
  type NetworkChangeListener = (payload: { isConnected: boolean }) => void;
2
2
  type VisibilityChangeListener = (payload: { isVisible: boolean }) => void;
3
3
  type MouseActiveListener = (payload: { isMouseActive: boolean }) => void;
4
+ type CCToggleListener = (payload: { isEnabled: boolean }) => void;
4
5
 
5
6
  export interface DeviceEventMap {
6
7
  'networkchange': NetworkChangeListener,
7
8
  'visibilitychange': VisibilityChangeListener,
8
9
  'mouseactive': MouseActiveListener,
10
+ 'cctoggle': CCToggleListener,
9
11
  }
@@ -0,0 +1,310 @@
1
+ import {
2
+ DeviceBase, ConnectionType, ScreenSaverStatus, VolumeRange,
3
+ } from '@24i/bigscreen-sdk/driver-base';
4
+ import { runAsync } from '@24i/bigscreen-sdk/utils/timers';
5
+ import { getKeyMap } from './keymap';
6
+ import { ISmartCastApi, IFA, DEFAULT_TTS_TIMEOUT, DevicePlaybackQuality } from './types';
7
+
8
+ declare const window: Window & { VIZIO: ISmartCastApi };
9
+
10
+ export class DeviceSmartCast extends DeviceBase {
11
+ keyMap = getKeyMap();
12
+
13
+ deviceId = '';
14
+
15
+ deviceModel = '';
16
+
17
+ deviceFirmware = '';
18
+
19
+ readonly backAsBackspaceInSearch = true;
20
+
21
+ devicePlaybackQualities: DevicePlaybackQuality[] = [];
22
+
23
+ closedCaptionsStatus = false;
24
+
25
+ textToSpeechStatus = false;
26
+
27
+ textToSpeechTimeout?: null | number;
28
+
29
+ lastTextToSpeechItem?: { delay: number, text: string } | null;
30
+
31
+ API?: ISmartCastApi;
32
+
33
+ IFA?: IFA;
34
+
35
+ async initDevice() {
36
+ this.initNetworkChangeListener();
37
+ this.initVisibilityChangeListener();
38
+ this.initMouseActiveListener();
39
+ await this.initPlatformApiListeners();
40
+ }
41
+
42
+ async initPlatformApiListeners(): Promise<void> {
43
+ return new Promise((resolve) => {
44
+ document.addEventListener('VIZIO_LIBRARY_DID_LOAD', async () => {
45
+ const API = window.VIZIO;
46
+ this.API = window.VIZIO;
47
+ const promises = [];
48
+
49
+ API.setClosedCaptionHandler((isEnabled: boolean) => {
50
+ this.closedCaptionsStatus = isEnabled;
51
+ this.triggerEvent('cctoggle', { isEnabled });
52
+ });
53
+
54
+ promises.push(new Promise<void>((r) => {
55
+ API.setAdvertiserIDListener((advertiserID: IFA) => {
56
+ this.IFA = advertiserID;
57
+ r();
58
+ });
59
+ }));
60
+
61
+ promises.push(new Promise<void>((r) => {
62
+ API.getDeviceId((id: string) => {
63
+ this.deviceId = id;
64
+ r();
65
+ });
66
+ }));
67
+
68
+ this.deviceModel = API.deviceModel;
69
+
70
+ promises.push(new Promise<void>((r) => {
71
+ API.getFirmwareVersion((version: string) => {
72
+ this.deviceFirmware = version;
73
+ r();
74
+ });
75
+ }));
76
+
77
+ promises.push(new Promise<void>((r) => {
78
+ API.getDevicePlaybackQualities((qualities: DevicePlaybackQuality[]) => {
79
+ this.devicePlaybackQualities = qualities;
80
+ r();
81
+ });
82
+ }));
83
+
84
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
85
+ API.setContentChangeHandler(this.handleDeeplink);
86
+
87
+ await Promise.all(promises);
88
+
89
+ resolve();
90
+ });
91
+
92
+ document.addEventListener('VIZIO_TTS_ENABLED', () => {
93
+ this.textToSpeechStatus = true;
94
+ }, false);
95
+
96
+ document.addEventListener('VIZIO_TTS_DISABLED', () => {
97
+ this.lastTextToSpeechItem = null;
98
+ this.textToSpeechStatus = false;
99
+ window.clearTimeout(this.textToSpeechTimeout as any);
100
+ this.API?.Chromevox.cancel();
101
+ }, false);
102
+
103
+ const object = document.createElement('script');
104
+ object.src = 'http://127.0.0.1:12345/scfs/cl/js/vizio-companion-lib.js';
105
+ document.body.appendChild(object);
106
+ });
107
+ }
108
+
109
+ initNetworkChangeListener() {
110
+ window.addEventListener('online', () => {
111
+ this.triggerEvent('networkchange', { isConnected: true });
112
+ });
113
+ window.addEventListener('offline', () => {
114
+ this.triggerEvent('networkchange', { isConnected: false });
115
+ });
116
+ }
117
+
118
+ initVisibilityChangeListener() {
119
+ document.addEventListener('visibilitychange', () => {
120
+ this.triggerEvent('visibilitychange', {
121
+ isVisible: document.visibilityState === 'visible',
122
+ });
123
+ });
124
+ }
125
+
126
+ initMouseActiveListener() {
127
+ document.addEventListener('mousemove', this.onMouseActive);
128
+ }
129
+
130
+ onMouseActive = () => {
131
+ document.removeEventListener('mousemove', this.onMouseActive);
132
+ document.addEventListener('keydown', this.onMouseInactive, true);
133
+ this.isMouseActive = true;
134
+ this.triggerEvent('mouseactive', { isMouseActive: true });
135
+ };
136
+
137
+ onMouseInactive = (event: KeyboardEvent) => {
138
+ if (!this.isDirectionalArrow(event)) return;
139
+ document.removeEventListener('keydown', this.onMouseInactive);
140
+ this.initMouseActiveListener();
141
+
142
+ // to let other handlers to use this property before change
143
+ runAsync(() => {
144
+ this.isMouseActive = false;
145
+ });
146
+ this.triggerEvent('mouseactive', { isMouseActive: false });
147
+ };
148
+
149
+ handleDeeplink = (deeplinkUrl: string) => {
150
+ if (typeof deeplinkUrl !== 'string') return;
151
+ const newRoute = deeplinkUrl.split('#')[1];
152
+ if (newRoute) {
153
+ const currentUrl = window.location.href.split('#')[0];
154
+ window.location.href = `${currentUrl}#${newRoute}`;
155
+ }
156
+ };
157
+
158
+ /**
159
+ * Reads provided text by the device internal reader.
160
+ */
161
+ readText({ text, delay = DEFAULT_TTS_TIMEOUT }: {
162
+ text: string, delay?: number
163
+ }) {
164
+ if (this.textToSpeechStatus && text) {
165
+ if (this.textToSpeechTimeout) {
166
+ this.lastTextToSpeechItem = { text, delay };
167
+ } else {
168
+ this.API?.Chromevox.play(text);
169
+
170
+ this.textToSpeechTimeout = window.setTimeout(() => {
171
+ this.textToSpeechTimeout = null;
172
+
173
+ if (this.lastTextToSpeechItem) {
174
+ const last = this.lastTextToSpeechItem;
175
+
176
+ this.API?.Chromevox.play(last.text);
177
+ this.lastTextToSpeechItem = null;
178
+ }
179
+ }, delay);
180
+ }
181
+ }
182
+ }
183
+
184
+ exit() {
185
+ this.API?.exitApplication();
186
+ }
187
+
188
+ reboot() {
189
+ window.location.reload();
190
+ }
191
+
192
+ /**
193
+ * Returs the status of Closed Captions feature.
194
+ */
195
+ async getClosedCaptionsStatus() {
196
+ return this.closedCaptionsStatus;
197
+ }
198
+
199
+ /**
200
+ * Returs the status of Text to Speech Engine if it is available by the device.
201
+ */
202
+ async getTextToSpeechStatus() {
203
+ return this.textToSpeechStatus;
204
+ }
205
+
206
+ /**
207
+ * Returns filled in Vast Tag URL.
208
+ */
209
+ getVastTagUrl(template: string): null | string {
210
+ const IFAObject = this.IFA;
211
+ const resolution = this.getResolution();
212
+
213
+ if (!IFAObject) return null;
214
+
215
+ return template
216
+ .replace('{{width}}', String(resolution.width))
217
+ .replace('{{height}}', String(resolution.height))
218
+ .replace('{{cb}}', String(Date.now()))
219
+ .replace('{{user_agent}}', encodeURIComponent(window.navigator.userAgent))
220
+ .replace('{{ifa}}', IFAObject.IFA)
221
+ .replace('{{ifa_type}}', IFAObject.IFA_TYPE)
222
+ .replace('{{lmt}}', String(IFAObject.LMT));
223
+ }
224
+
225
+ async getManufacturerName() {
226
+ return 'VIZIO';
227
+ }
228
+
229
+ async getPlatformName() {
230
+ return 'SmartCast';
231
+ }
232
+
233
+ async getPlatformVersion() {
234
+ return '';
235
+ }
236
+
237
+ async getDeviceName() {
238
+ return 'VIZIO SmartCast';
239
+ }
240
+
241
+ async getModelName() {
242
+ return this.deviceModel;
243
+ }
244
+
245
+ async getFirmware() {
246
+ return this.deviceFirmware;
247
+ }
248
+
249
+ async is4KSupported() {
250
+ return this.devicePlaybackQualities.includes('UHD');
251
+ }
252
+
253
+ async is8KSupported() {
254
+ return false;
255
+ }
256
+
257
+ async getUuid() {
258
+ return this.deviceId;
259
+ }
260
+
261
+ async getVolume() {
262
+ return VolumeRange.UNSUPPORTED;
263
+ }
264
+
265
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
266
+ async setVolume(volumePercentage: number) {
267
+ // not supported on SmartCast platform
268
+ }
269
+
270
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
271
+ async mute(mute: boolean) {
272
+ // not supported on SmartCast platform
273
+ }
274
+
275
+ async isMute() {
276
+ return false;
277
+ }
278
+
279
+ async getScreenSaver() {
280
+ return { enabled: false };
281
+ }
282
+
283
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
284
+ async setScreenSaver(status: ScreenSaverStatus) {
285
+ // not supported on SmartCast platform
286
+ }
287
+
288
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
289
+ async setUserAgent(userAgent: string) {
290
+ return false;
291
+ }
292
+
293
+ async getConnectionType() {
294
+ return 'unknown' as ConnectionType;
295
+ }
296
+
297
+ async isNetworkConnected() {
298
+ return window.navigator.onLine;
299
+ }
300
+
301
+ async getLocalIP() {
302
+ // not supported on SmartCast platform
303
+ return '';
304
+ }
305
+
306
+ async getMacAddress() {
307
+ // not supported on SmartCast platform
308
+ return '';
309
+ }
310
+ }
@@ -0,0 +1,49 @@
1
+ import { IFA, ISmartCastApi, DevicePlaybackQuality } from '../types';
2
+
3
+ export const deviceId = '2bd671ef-02cd-445f-9295-b3565dbaf1e8';
4
+ export const deviceModel = 'D24f4-J01';
5
+ export const firmware = '3.520.28.1-2';
6
+ export const playbackQualities = ['UHD', 'HD'] as DevicePlaybackQuality[];
7
+ // eslint-disable-next-line max-len
8
+ export const deeplinkUrl = 'https://smartapps-bigscreen.24i.com/#/detail/SERIES/FO03OogBXRsigNB1VRz_/undefined/undefined/';
9
+ // eslint-disable-next-line max-len
10
+ export const deeplinkUrl2 = 'https://smartapps-bigscreen.24i.com/#/detail/SERIES/Ge1sO4gBXRsigNB1TBwy/undefined/undefined/';
11
+ // eslint-disable-next-line max-len
12
+ export const vastTagTemplate = 'w={{width}}&h={{height}}&did={{ifa}}&ifa_type={{ifa_type}}&lmt={{lmt}}';
13
+
14
+ export const SmartCastMockApi: ISmartCastApi = {
15
+ deviceModel,
16
+
17
+ getDeviceId: (callback: (id: string) => void): void => callback(deviceId),
18
+
19
+ getFirmwareVersion: (callback: (id: string) => void): void => callback(firmware),
20
+
21
+ getDevicePlaybackQualities: (
22
+ callback: (
23
+ qualities: DevicePlaybackQuality[]
24
+ ) => void,
25
+ ): void => callback(playbackQualities),
26
+
27
+ exitApplication: (): void => {},
28
+
29
+ setClosedCaptionHandler: (callback: (isEnabled: boolean) => void): void => callback(false),
30
+
31
+ setAdvertiserIDListener: (callback: (advertiserID: IFA) => void): void => callback({
32
+ IFA: deviceId,
33
+ IFA_TYPE: 'vida',
34
+ LMT: 0,
35
+ }),
36
+
37
+ setContentChangeHandler:
38
+ (callback: (contentUrl: string) => void): void => callback(deeplinkUrl),
39
+
40
+ Chromevox: {
41
+ cancel: (): void => {
42
+ throw new Error('Function not implemented.');
43
+ },
44
+ play: (text: string): void => {
45
+ // eslint-disable-next-line no-console
46
+ console.log(text);
47
+ },
48
+ },
49
+ };
@@ -0,0 +1 @@
1
+ export { DeviceSmartCast } from './DeviceSmartCast';
@@ -0,0 +1,9 @@
1
+ import { generateKeyCodeKeyMap, overrideValues } from '@24i/bigscreen-sdk/driver-base/keymap';
2
+
3
+ export const getKeyMap = () => {
4
+ overrideValues({
5
+ CHANNEL_PREVIOUS: { code: 'PrevCh', keyCode: 500 },
6
+ SUBTITLES: { keyCode: 204 },
7
+ });
8
+ return generateKeyCodeKeyMap();
9
+ };
@@ -0,0 +1,65 @@
1
+ export type ISmartCastApi = {
2
+ /**
3
+ * Sets the callback to receive the device UID in standard 32 digits.
4
+ */
5
+ getDeviceId: (callback: (id: string) => void) => void;
6
+
7
+ /**
8
+ * Sets the callback to receive the device firmware version.
9
+ */
10
+ getFirmwareVersion: (callback: (firmwareVersion: string) => void) => void;
11
+
12
+ /**
13
+ * Sets the callback to receive the device playback qualities.
14
+ */
15
+ getDevicePlaybackQualities: (callback: (qualities: DevicePlaybackQuality[]) => void) => void;
16
+
17
+ /**
18
+ * Exits the application.
19
+ */
20
+ exitApplication: () => void;
21
+
22
+ /**
23
+ * Sets the callback to receive the device CC toggle event.
24
+ */
25
+ setClosedCaptionHandler: (callback: (isEnabled: boolean) => void) => void;
26
+
27
+ /**
28
+ * Sets the callback to receive IFA object with the device ads related parameters.
29
+ */
30
+ setAdvertiserIDListener: (callback: (advertiserID: IFA) => void) => void;
31
+
32
+ /**
33
+ * Sets the callback to receive the device content change event.
34
+ */
35
+ setContentChangeHandler: (callback: (contentUrl: string) => void) => void;
36
+
37
+ /**
38
+ * Device model.
39
+ */
40
+ deviceModel: string;
41
+
42
+ /**
43
+ * Chromevox Text-to-Speech accessibility playback engine.
44
+ */
45
+ Chromevox: {
46
+ cancel: () => void,
47
+ play: (text: string) => void,
48
+ };
49
+ };
50
+
51
+ /**
52
+ * Identifier For Advertising (IFA) and Limited Tracking parameter object
53
+ */
54
+ export type IFA = {
55
+ /** Unique identifier string for advertising */
56
+ IFA: string,
57
+ /** String value for IFA type ("vida") */
58
+ IFA_TYPE: string,
59
+ /** O or 1 (1=Limited tracking enabled, 0=Limited tracking disabled) */
60
+ LMT: 0 | 1
61
+ };
62
+
63
+ export type DevicePlaybackQuality = 'UHD' | 'HD' | 'SD';
64
+
65
+ export const DEFAULT_TTS_TIMEOUT = 3000;
@@ -1,5 +1,5 @@
1
1
  import { Component, createRef, Reference } from '@24i/bigscreen-sdk/jsx';
2
- import { getKeyDigit } from '@24i/bigscreen-sdk/device';
2
+ import { getKeyDigit, device } from '@24i/bigscreen-sdk/device';
3
3
  import { isRtl } from '@24i/bigscreen-sdk/i18n';
4
4
  import { EventsManager } from '@24i/bigscreen-sdk/events-manager';
5
5
  import {
@@ -121,8 +121,15 @@ export abstract class KeyboardBase<
121
121
  */
122
122
  onKeyDown(event: KeyboardEvent) {
123
123
  const { handleRemoteDigits, handleHWKeyboard } = this.props;
124
+ const preventBack = 'backAsBackspaceInSearch' in device && !!device.backAsBackspaceInSearch;
125
+ const inputLength = this.inputElement?.value?.length;
126
+
124
127
  if (BACK.is(event)) {
125
- this.onCancel();
128
+ if (preventBack && inputLength) {
129
+ this.onBackspace();
130
+ } else {
131
+ this.onCancel();
132
+ }
126
133
  } else {
127
134
  let char: string | null = null;
128
135
  if (handleHWKeyboard) {
@@ -30,7 +30,6 @@ export class CenteredListController<T> extends UnifiedListController<T, Props<an
30
30
  this.scrollListScroller(-this.topOffset);
31
31
  if (initialFocusIndex !== 0) {
32
32
  base.index = initialFocusIndex!;
33
- this.focusCurrentItem();
34
33
  this.scroll(base.index, false);
35
34
  }
36
35
  }