/*
* Copyright: IRT 2019
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var Emitter = require('events').EventEmitter;
var inherit = require('../tools/inherit');
var instance = null;
/**
* Instances of this class provide details of endpoints of a Terminal that has
* been discovered using the discoverTerminals()
*
* @typedef {object} DiscoveredCSLauncher
* @property {number} enum_id The unique ID of an instance of a CS Launcher application.
* @property {number} friendly_name A CS Launcher application may provide a friendly name.
* @property {number} CS_OS_id The CS OS identifier string, as described in clause 14.4.1. of the HbbTV 2 specification
*/
/**
* An error occured.
*
* <p>Use the [CSMan.EVT_CS_ERROR]{@link CSMan#EVT_CS_ERROR} event name constant
* to listen for this event.
*
* @event CSMan#EVT_CS_ERROR
* @param {string} errorCode Error code. Following values possible:
* <ul>
* <li>[ERR_UNDEFINED]{@link CSMan#ERR_UNDEFINED}
* <li>[ERR_CSMAN_NOTFOUND]{@link CSMan#ERR_CSMAN_NOTFOUND}
* <li>[ERR_INVALID_ENUM_ID]{@link CSMan#ERR_INVALID_ENUM_ID}
* <li>[ERR_CSMAN_TIMEOUT]{@link CSMan#ERR_CSMAN_TIMEOUT}
* </ul>
* @param {string} message A textual description of what happened
*/
/**
* A companion screen has been discovered or the discovery timed out.
*
* <p>Use the [CSMan.EVT_CS_DISCOVERED]{@link CSMan#EVT_CS_DISCOVERED} event name constant
* to listen for this event.
*
* @event CSMan#EVT_CS_DISCOVERED
* @param {Array<DiscoveredCSLauncher>} csLaunchers Array of discovered CS launchers.
*/
/**
* Response of a launch or install request is available.
*
* @event CSMan#EVT_CS_LAUNCH_RESPONSE
* @param {number} responseCode Can have the following values:
* <ul>
* <li>[OP_REJECTED]{@link CSMan#OP_REJECTED}
* <li>[OP_DENIED]{@link CSMan#OP_DENIED}
* <li>[INVALID_ID]{@link CSMan#INVALID_ID}
* <li>[GENERAL_ERROR]{@link CSMan#GENERAL_ERROR}
* <li>[OP_FAILED]{@link CSMan#OP_FAILED}
* <li>[APP_ALREADY_INSTALLED]{@link CSMan#APP_ALREADY_INSTALLED}
* </ul>
*/
/**
* @exports CSMan
* @class CSMan
* @hideconstructor
*
* @classdesc Module for using the Companion Screen Manager API of HbbTV 2.0
*
* <p>CSMan may fire the following events:
* <ul>
* <li>[EVT_CS_ERROR]{@link CSMan#event:EVT_CS_ERROR}</li>
* <li>[EVT_CS_DISCOVERED]{@link CSMan#event:EVT_CS_DISCOVERED}</li>
* <li>[EVT_CS_LAUNCH_RESPONSE]{@link CSMan#event:EVT_CS_LAUNCH_RESPONSE}</li>
* </ul>
*
* @example <caption>Launch a webpage with the remote app2app service endpoint added to the URL.</caption>
* var CSMan = require("hbbtv-lib/cs/cs_manager");
*
* CSMan.once(CSMan.CS_DISCOVERED, on_discovery_finished);
* CSMan.discover_launchers(10000);
*
* function on_discovery_finished (devices) {
* for (x in devices) {
* CSMan.launch_html(devices[x].enum_id, "http://example.com/");
* }
* }
*
* @augments external:EventEmitter
*/
function CSMan () {
try {
this.cs_man = oipfObjectFactory.createCSManager();
if (!this.cs_man || typeof this.cs_man.discoverCSLaunchers != "function") {
this.cs_man = null;
}
} catch (e) {
}
}
inherit(CSMan, Emitter);
/**
* Get the current base URL of the local endpoint of the application to application
* communication service.
*
* <p>The endpoint is defined in clause 14.5.2 of the
* [HbbTV 2 specification]{@link https://www.hbbtv.org/wp-content/uploads/2018/02/HbbTV_v202_specification_2018_02_16.pdf}.
* The usage of this endpoint to communicate between the HbbTV® application and the
* remote client is described in clause 14.5.1. The URL retrieved by this method shall
* end with a slash ('/') character.
*
* @returns {string} the local app2app service endpoint.
*/
CSMan.prototype.getA2A_local = function () {
try {
return this.cs_man.getApp2AppLocalBaseURL();
} catch (e) {}
return null;
};
/**
* Get the current base URL of the remote endpoint of the application to application service.
*
* <p>The URL retrieved by this method shall be the same as the URL carried
* in the <X_HbbTV_App2AppURL> element as described in clause 14.7.2 of the
* [HbbTV 2 specification]{@link https://www.hbbtv.org/wp-content/uploads/2018/02/HbbTV_v202_specification_2018_02_16.pdf}
* and shall end with a slash ('/') character.
*
* @returns {string} the remote app2app service endpoint, i.e. the one used by the companion application
*/
CSMan.prototype.getA2A_remote = function () {
try {
return this.cs_man.getApp2AppRemoteBaseURL();
} catch (e) {}
return null;
}
/**
* Get the URL that points to the CSS-CII service endpoint of the terminal that the calling
* HbbTV application is running on.
*
* <p>The URL retrieved by this method shall be the same as the URL carried in the
* <X_HbbTV_InterDevSyncURL> element as described in clause 14.7.2. of the
* [HbbTV 2 specification]{@link https://www.hbbtv.org/wp-content/uploads/2018/02/HbbTV_v202_specification_2018_02_16.pdf}
*
* @returns {string} the DVB CSS CII service endpoint.
*/
CSMan.prototype.getCII = function () {
try {
return this.cs_man.getInterDevSyncURL ();
} catch (e) {}
return null;
}
/**
* Discovers available CS launchers.
*
* @param {number} [timeout=1500] Specifies when discovery shall terminate.
*
* @fires CSMan#EVT_CS_DISCOVERED
* @fires CSMan#EVT_CS_ERROR
*/
CSMan.prototype.discover_launchers = function (timeout) {
var _timeout = timeout || 1500;
var timer = null;
var that = this;
if (this.cs_man && this.cs_man.discoverCSLaunchers(function (arr) {
if (timer) {
clearTimeout(timer);
timer = null;
that.emit(that.EVT_CS_DISCOVERED, arr);
} else {
that.emit(that.EVT_CS_ERROR, that.ERR_UNDEFINED, "CS Manager: onCSDiscovery called after time out.");
}
})) {
timer = setTimeout (
function () {
timer = null;
that.emit(that.EVT_CS_ERROR, that.ERR_CSMAN_TIMEOUT, "CS Manager: discovery timed out.");
}, _timeout
);
return true;
} else {
this.emit(this.EVT_CS_ERROR, this.ERR_CSMAN_NOTFOUND, "CSManager not available.");
return false;
}
}
/**
* Send launch and install requests.
*
* @param {number} enum_id Identifier (enum_id) of a [DiscoveredCSLauncher]{@link DiscoveredCSLauncher}
* @param {string} payload Information on the app to be launched.
*
* @returns {boolean} TRUE if request was executed, else FALSE.
*
* @fires CSMan#EVT_CS_LAUNCH_RESPONSE
* @fires CSMan#EVT_CS_ERROR
*
* @see Section 14.4.2. of the [HbbTV 2 specification]{@link https://www.hbbtv.org/wp-content/uploads/2018/02/HbbTV_v202_specification_2018_02_16.pdf}
* for the definition of the payload format.
*/
CSMan.prototype.send_request = function (enum_id, request) {
var that = this;
if (this.cs_man.launchCSApp(enum_id, request,
function (enum_id, error_code) {
that.emit(that.EVT_CS_LAUNCH_RESPONSE, enum_id, error_code);
}
)) {
return true;
}
this.emit(this.EVT_CS_ERROR, this.ERR_INVALID_ENUM_ID, enum_id, "CSManager: launch failed, enum_id not valid: " + enum_id);
return false;
}
CSMan.prototype.to_launch_struct = function (url, type) {
return {"launchUrl": url, "appType": type};
}
/**
* Launches an HTML page on the selected device.
*
* @param {number} enum_id Identifier (enum_id) of a [DiscoveredCSLauncher]{@link DiscoveredCSLauncher}
* @param {string} url a URL to a web site that shall be launched.
*
* @returns {boolean} TRUE if request was executed, else FALSE.
*
* @fires CSMan#EVT_CS_LAUNCH_RESPONSE
* @fires CSMan#EVT_CS_ERROR
*/
CSMan.prototype.launch_html = function (enum_id, url) {
if (!this.cs_man) {
this.emit(this.CS_ERROR, this.ERR_CSMAN_NOTFOUND, "CSManager not available.");
return false;
}
return this.send_request(enum_id, JSON.stringify(
{
"launch": [this.to_launch_struct(url, "html")]
}
));
}
/**
* Launches a native application on the selected device.
*
* @param {number} enum_id Identifier (enum_id) of a [DiscoveredCSLauncher]{@link DiscoveredCSLauncher}
* @param {string} url a URL that is connected with a native application, that is installed on the companion device.
*
* @returns {boolean} TRUE if request was executed, else FALSE.
*
* @fires CSMan#EVT_CS_LAUNCH_RESPONSE
* @fires CSMan#EVT_CS_ERROR
*/
CSMan.prototype.launch_native = function (enum_id, url) {
if (!this.cs_man) {
this.emit(this.CS_ERROR, this.ERR_CSMAN_NOTFOUND, "CSManager not available.");
return false;
}
return this.send_request(enum_id, JSON.stringify(
{
"launch": [this.to_launch_struct(url, "native")]
}
));
}
/**
* Launches a native application with an HTML page as fallback on the selected device.
*
* @param {number} enum_id Identifier (enum_id) of a [DiscoveredCSLauncher]{@link DiscoveredCSLauncher}
* @param {string} url_native a URL that is connected with a native application, that is installed on the companion device.
* @param {string} url_html a URL to a web site that shall be launched if the native application can't be launched.
*
* @returns {boolean} TRUE if request was executed, else FALSE.
*
* @fires CSMan#EVT_CS_LAUNCH_RESPONSE
* @fires CSMan#EVT_CS_ERROR
*/
CSMan.prototype.launch_native_html = function (enum_id, url_native, url_html) {
if (!this.cs_man) {
this.emit(this.CS_ERROR, this.ERR_CSMAN_NOTFOUND, "CSManager not available.");
return false;
}
return this.send_request(enum_id, JSON.stringify(
{
"launch": [
this.to_launch_struct(url_native, "native"),
this.to_launch_struct(url_html, "html")
]
}
));
}
/**
* Issues an install request on the selected device.
*
* @param {number} enum_id Identifier (enum_id) of a [DiscoveredCSLauncher]{@link DiscoveredCSLauncher}
* @param {string} app_url market specific identifier of an application
* @param {string} market_id id of a supported market on the companion device, e.g. Play Store, etc.
*
*
* @returns {boolean} TRUE if request was executed, else FALSE.
*
* @fires CSMan#EVT_CS_LAUNCH_RESPONSE
* @fires CSMan#EVT_CS_ERROR
*/
CSMan.prototype.install = function (enum_id, app_url, market_id) {
if (!this.cs_man) {
this.emit(this.CS_ERROR, this.ERR_CSMAN_NOTFOUND, "CSManager not available.");
return false;
}
return this.send_request(enum_id, JSON.stringify(
{
"install": [
{
"installUrl" : app_url,
"appStoreId" : market_id
}
]
}
));
}
/**
* Event name constant for the [EVT_CS_ERROR]{@link CSMan#event:EVT_CS_ERROR} event.
*/
CSMan.prototype.EVT_CS_ERROR = "error";
CSMan.prototype.CS_ERROR = CSMan.prototype.EVT_CS_ERROR; // Backward compatibility
/**
* Event name constant for the [EVT_CS_DISCOVERED]{@link CSMan#event:EVT_CS_DISCOVERED} event.
*/
CSMan.prototype.EVT_CS_DISCOVERED = "cs_discovered";
CSMan.prototype.CS_DISCOVERED = CSMan.prototype.EVT_CS_DISCOVERED; // Backward compatibility
/**
* Event name constant for the [EVT_CS_LAUNCH_RESPONSE]{@link CSMan#event:EVT_CS_LAUNCH_RESPONSE} event.
*/
CSMan.prototype.EVT_CS_LAUNCH_RESPONSE = "cs_launch_resp";
CSMan.prototype.CS_LAUNCH_RESPONSE = CSMan.prototype.EVT_CS_LAUNCH_RESPONSE;
/**
* Unspecified error.
*/
CSMan.prototype.ERR_UNDEFINED = 0;
/**
* HbbTV CS Manager could not be initialized.
*/
CSMan.prototype.ERR_CSMAN_NOTFOUND = 1;
/**
* Error signalled if the enum_id in a launch or install request does not match a connected CS launcher.
*/
CSMan.prototype.ERR_INVALID_ENUM_ID = 2;
/**
* The discovery for launcher apps timed out. HbbTV requires that the discovery is finished within a second.
* @see CSMan.discover_launchers()
*/
CSMan.prototype.ERR_CSMAN_TIMEOUT = 3;
// Error codes fuer launch_and_install
/**
* The CS Launcher application has automatically rejected
* the operation with no interaction with the user of the
* Companion Screen.
* This error code is intended for use when the CS Launcher
* has automatically blocked the operation due to a blacklist
* feature.
*/
CSMan.prototype.OP_REJECTED = 0;
/**
* The CS Launcher application has blocked the operation,
* but it was blocked by the explicit interaction of the user of
* the Companion Screen.
*/
CSMan.prototype.OP_DENIED = 1;
/**
* The CS Launcher application has initiated the instruction
* (launch or install) without a problem. It is assumed (to the
* best knowledge of the Launcher application) that the
* launch or installation operation has completed
* successfully.
*/
CSMan.prototype.OP_NOT_GUARANTEED = 2;
/**
* The CS Launcher application that is identified by enum_id
* is no longer available. (i.e. it has become unavailable
* since discovery occurred), or has never been available.
*/
CSMan.prototype.INVALID_ID = 3;
/**
* A general error has occurred that meant that the operation
* could not be completed for a reason other than those
* described for the other error codes in this table.
*/
CSMan.prototype.GENERAL_ERROR = 4;
/**
* The CS Launcher application attempted a Launch or
* Install operation but it is known to have failed. For a Native
* application, it means that the application could not be
* launched or installed. For an HTML application it means
* that an HTML application environment could not be
* launched.
*/
CSMan.prototype.OP_FAILED = 5;
/**
* The CS Launcher application received an Install operation
* (without a Launch operation) but it detected that the
* requested application is already installed
*/
CSMan.prototype.APP_ALREADY_INSTALLED = 6;
module.exports = {
getInstance : function () {
if (instance === null) {
instance = new CSMan();
}
return instance;
}
};