UNPKG

@swrve/smarttv-sdk

Version:

Swrve marketing engagement platform SDK for SmartTV OTT devices

338 lines (286 loc) 9.89 kB
import { IPlatform, IPlatformName, DevicePropertyName, NetworkListener, NetworkMonitorHandle, NETWORK_CONNECTED, NETWORK_DISCONNECTED, NetworkStatus, } from "./IPlatform"; import {generateUuid} from '../uuid'; import {IKeyMapping } from "./IKeymapping"; import SwrveLogger from "../SwrveLogger"; import {IAsset} from "./IAsset"; import ISwrveConfig from "../../Config/ISwrveConfig"; const protectedKeyPrefix = /^swrve\./; export function clearLocalStorage(force: boolean = false): void { const keys = []; for (let i = 0; i < localStorage.length; i += 1) { const key = localStorage.key(i); if (key && force) { keys.push(key); } else if (key && !protectedKeyPrefix.test(key)) { keys.push(key); } } keys.forEach(key => localStorage.removeItem(key!)); } if (typeof window !== "undefined" && window.localStorage && window.localStorage.clear !== clearLocalStorage) { window.localStorage.clear = clearLocalStorage; } /** * Base Platform (also the browser platform) */ export default class BasePlatform implements IPlatform { protected defaultMapping: IKeyMapping = { 36: "Return", 38: "Up", 40: "Down", 37: "Left", 39: "Right", 13: "Enter", 65: "A", 66: "B", 67: "C", 68: "D", 8: "Back", 179: "Play", 227: "FastForward", 228: "Rewind", 112: "F1", }; /** True if the platform needs a proxy. */ protected needsProxy: boolean = true; /** True if this platform supports the magic wand. */ protected supportsMagicWandNatively: boolean = false; /** Number of history entries on start. */ protected startHistoryLength: number = 0; protected _firmware: string | undefined; protected _deviceID: string | undefined; protected _model: string | undefined; protected _os: string | undefined; protected _osVersion: string | undefined; protected _language: string | undefined; protected _countryCode: string | undefined; protected _screenDPI: number | undefined; protected _screenHeight: number | undefined; protected _screenWidth: number | undefined; protected _timezone: string | undefined; protected _region: string | undefined; protected networkMonitorHandle?: NetworkMonitorHandle; protected networkListeners: NetworkListener[] = []; protected config: ISwrveConfig | undefined; constructor(config?: Readonly<ISwrveConfig>) { if (config) { this.config = config; if (config.customKeyMappingBase) { this.defaultMapping = config.customKeyMappingBase; } } } public name(): IPlatformName { if (this.config?.customDeviceName) { return this.config?.customDeviceName; } else { return { name: "Browser", variation: "Base", }; } } public init(deviceProperties: ReadonlyArray<DevicePropertyName>): Promise<void> { if (typeof window !== "undefined") { this.startHistoryLength = window.history.length; } const cache = document.createElement("div"); cache.id = "PALImageCache"; cache.style.overflow = "hidden"; cache.style.position = "absolute"; cache.style.left = "-10000px"; cache.style.width = "1px"; cache.style.height = "1px"; if (document.getElementById("PALImageCache") === null) { document.body.appendChild(cache); } SwrveLogger.info("PAL init"); return Promise.resolve(); } public monitorNetwork(networkListener: NetworkListener): NetworkListener { if (this.networkMonitorHandle === undefined) { this.networkMonitorHandle = this.initNetworkListener(); } this.networkListeners.push(networkListener); return networkListener; } public stopMonitoringNetwork(networkListener: NetworkListener): void { this.networkListeners = this.networkListeners.filter(listener => listener !== networkListener); if (this.networkListeners.length === 0 && this.networkMonitorHandle !== undefined) { this.removeNetworkListener(this.networkMonitorHandle); delete this.networkMonitorHandle; } } public getNeedsProxy(): boolean { return this.needsProxy; } public getSupportsMagicWandNatively(): boolean { return this.supportsMagicWandNatively; } public disableScreenSaver(): void { SwrveLogger.error("platform does not know how to disable screensaver"); } public openLink(link: string): void { if (typeof window !== "undefined") { window.open(link); } } public enableScreenSaver(): void { SwrveLogger.error("platform does not know how to enable screensaver"); } public get synchronousStorage(): Storage | null { if (typeof window !== "undefined") { return window.localStorage; } else { return null; } } public downloadAssets(assets: ReadonlyArray<IAsset>): Promise<void> { const downloading = assets.map((asset) => { const img = document.createElement("img"); img.src = asset.path; SwrveLogger.info("PAL download " + asset.path); const imageCache = document.getElementById("PALImageCache"); if (imageCache) { imageCache.appendChild(img); } else { SwrveLogger.info(" PAL: Image cache does not exist"); } return new Promise<void>( (resolve, reject) => { img.addEventListener("load", () => { resolve(); }); img.addEventListener("error", () => { reject(); }); }); }); return Promise.all(downloading) .then(() => void 0); } public exit(): void { const backlength = window.history.length - this.startHistoryLength - 1; window.history.go(-backlength); } public get deviceID(): string { const localStorage = this.synchronousStorage; if (localStorage) { let saved = localStorage.getItem("swrve.deviceid"); if (!saved) { saved = generateUuid().toString(); localStorage.setItem("swrve.deviceid", saved); } return saved; } else { return ""; } } public get firmware(): string { return 'Not Supported'; } public get timezone(): string { if (this._timezone === undefined) { this._timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; } return this._timezone || ""; } public get region(): string { if (this._region === undefined) { this._region = this.timezone.indexOf('/') !== -1 ? this.timezone.split('/')[0] : ""; } return this._region || ""; } public getDeviceBrowserVersion(): string | null { const match = navigator == null || navigator.appVersion == null ? null : navigator.appVersion.match(/^[^\s]*/); return match == null ? null : match[0]; } public get model(): string { return ""; } public get os(): string { return this.config?.customOS ?? "web"; } public get osVersion(): string { if (this.config?.customOSVersion) { return this.config?.customOSVersion; } else { const match = navigator.userAgent.match(/[^\s]+$/); return match ? match[0] : "Unknown version"; } } public get appStore(): string { return this.config?.customAppStore ?? "google"; } public get language(): string { if (this._language === undefined && typeof navigator !== "undefined") { this._language = navigator.language.split('-')[0]; } return this._language || ""; } public get countryCode(): string { if (this._countryCode === undefined && typeof navigator !== "undefined") { this._countryCode = navigator.language.split('-')[1]; } return this._countryCode || ""; } public get screenDPI(): number { if (this._screenDPI === undefined) { this._screenDPI = calculatePPI(this.screenWidth, this.screenHeight, detectScreenDiagonal(this.model)); } return this._screenDPI; } public get screenHeight(): number { if (this._screenHeight === undefined) { this._screenHeight = typeof navigator !== "undefined" ? window.innerHeight : 0; } return this._screenHeight || 0; } public get screenWidth(): number { if (this._screenWidth === undefined) { this._screenWidth = typeof navigator !== "undefined" ? window.innerWidth : 0; } return this._screenWidth || 0; } public supportsHDR(): boolean { return false; } public getKeymapping(): IKeyMapping { return this.defaultMapping; } protected triggerNetworkChange(status: NetworkStatus): void { this.networkListeners.forEach(listener => listener(status)); } protected initNetworkListener(): NetworkMonitorHandle { const onOnline = () => this.triggerNetworkChange(NETWORK_CONNECTED); const onOffline = () => this.triggerNetworkChange(NETWORK_DISCONNECTED); window.addEventListener('offline', onOffline); window.addEventListener('online', onOnline); return [onOnline, onOffline]; } protected removeNetworkListener(handle: NetworkMonitorHandle): void { const [onOnline, onOffline] = <[() => void, () => void]> handle; window.removeEventListener('offline', onOffline); window.removeEventListener('online', onOnline); } } // First numeric part of Samsung and LG model names indicates screen size in inches export function detectScreenDiagonal(modelName: string): number { let size; const match = modelName.match(/\d+/); if (match) { size = parseInt(match[0], 10); } // fallback to median screen size on the market return size || 50; } export function calculatePPI(pixelWidth: number, pixelHeight: number, inchDiagonal: number): number { const pixelDiagonal = Math.sqrt((pixelWidth * pixelWidth) + (pixelHeight * pixelHeight)); return Math.round(pixelDiagonal / inchDiagonal); }