diff --git a/app.js b/app.js index d8e73ae53..9207cf6ca 100644 --- a/app.js +++ b/app.js @@ -22,8 +22,7 @@ import conference from './conference'; import API from './modules/API/API'; import translation from "./modules/translation/translation"; -// For remote control testing: -// import remoteControlController from "./modules/remotecontrol/Controller"; +import remoteControl from "./modules/remotecontrol/remotecontrol"; const APP = { // Used by do_external_connect.js if we receive the attach data after @@ -61,7 +60,8 @@ const APP = { */ ConferenceUrl : null, connection: null, - API + API, + remoteControl }; // TODO The execution of the mobile app starts from react/index.native.js. diff --git a/conference.js b/conference.js index 73e762d3d..a51192de9 100644 --- a/conference.js +++ b/conference.js @@ -488,6 +488,7 @@ export default { }).then(([tracks, con]) => { logger.log('initialized with %s local tracks', tracks.length); APP.connection = connection = con; + APP.remoteControl.init(); this._bindConnectionFailedHandler(con); this._createRoom(tracks); this.isDesktopSharingEnabled = diff --git a/modules/API/API.js b/modules/API/API.js index b988c089f..b9860d09b 100644 --- a/modules/API/API.js +++ b/modules/API/API.js @@ -55,7 +55,9 @@ function initCommands() { APP.conference.toggleScreenSharing.bind(APP.conference), "video-hangup": () => APP.conference.hangup(), "email": APP.conference.changeLocalEmail, - "avatar-url": APP.conference.changeLocalAvatarUrl + "avatar-url": APP.conference.changeLocalAvatarUrl, + "remote-control-supported": isSupported => + APP.remoteControl.onRemoteControlSupported(isSupported) }; Object.keys(commands).forEach(function (key) { postis.listen(key, args => commands[key](...args)); @@ -94,7 +96,13 @@ function triggerEvent (name, object) { } } -export default { +class API { + /** + * Constructs new instance + * @constructor + */ + constructor() { } + /** * Initializes the APIConnector. Setups message event listeners that will * receive information from external applications that embed Jitsi Meet. @@ -108,6 +116,13 @@ export default { return; enabled = true; + } + + /** + * initializes postis library. + * @private + */ + _initPostis() { let postisOptions = { window: target }; @@ -116,7 +131,7 @@ export default { = "jitsi_meet_external_api_" + jitsi_meet_external_api_id; postis = postisInit(postisOptions); initCommands(); - }, + } /** * Notify external application (if API is enabled) that message was sent. @@ -124,7 +139,7 @@ export default { */ notifySendingChatMessage (body) { triggerEvent("outgoing-message", {"message": body}); - }, + } /** * Notify external application (if API is enabled) that @@ -143,7 +158,7 @@ export default { "incoming-message", {"from": id, "nick": nick, "message": body, "stamp": ts} ); - }, + } /** * Notify external application (if API is enabled) that @@ -152,7 +167,7 @@ export default { */ notifyUserJoined (id) { triggerEvent("participant-joined", {id}); - }, + } /** * Notify external application (if API is enabled) that @@ -161,7 +176,7 @@ export default { */ notifyUserLeft (id) { triggerEvent("participant-left", {id}); - }, + } /** * Notify external application (if API is enabled) that @@ -171,7 +186,7 @@ export default { */ notifyDisplayNameChanged (id, displayName) { triggerEvent("display-name-change", {id, displayname: displayName}); - }, + } /** * Notify external application (if API is enabled) that @@ -181,7 +196,7 @@ export default { */ notifyConferenceJoined (room) { triggerEvent("video-conference-joined", {roomName: room}); - }, + } /** * Notify external application (if API is enabled) that @@ -191,7 +206,7 @@ export default { */ notifyConferenceLeft (room) { triggerEvent("video-conference-left", {roomName: room}); - }, + } /** * Notify external application (if API is enabled) that @@ -199,7 +214,7 @@ export default { */ notifyReadyToClose () { triggerEvent("video-ready-to-close", {}); - }, + } /** * Sends remote control event. @@ -207,13 +222,15 @@ export default { */ sendRemoteControlEvent(event) { sendMessage({method: "remote-control-event", params: event}); - }, + } /** * Removes the listeners. */ - dispose: function () { + dispose () { if(enabled) postis.destroy(); } -}; +} + +export default new API(); diff --git a/modules/remotecontrol/Controller.js b/modules/remotecontrol/Controller.js index 5650b4c7f..56b739f11 100644 --- a/modules/remotecontrol/Controller.js +++ b/modules/remotecontrol/Controller.js @@ -1,5 +1,7 @@ /* global $, APP */ import * as KeyCodes from "../keycode/keycode"; +import {EVENT_TYPES, API_EVENT_TYPE} + from "../../service/remotecontrol/Constants"; /** * Extract the keyboard key from the keyboard event. @@ -42,11 +44,21 @@ function getModifiers(event) { * It listens for mouse and keyboard events and sends them to the receiver * party of the remote control session. */ -class Controller { +export default class Controller { /** * Creates new instance. */ - constructor() {} + constructor() { + this.enabled = false; + } + + /** + * Enables / Disables the remote control + * @param {boolean} enabled the new state. + */ + enable(enabled) { + this.enabled = enabled; + } /** * Starts processing the mouse and keyboard events. @@ -54,29 +66,34 @@ class Controller { * attaching the listeners on. */ start(area) { + if(!this.enabled) + return; this.area = area; this.area.mousemove(event => { const position = this.area.position(); - this._sendEvent({ - type: "mousemove", + this._sendRemoteControlEvent({ + type: EVENT_TYPES.mousemove, x: (event.pageX - position.left)/this.area.width(), y: (event.pageY - position.top)/this.area.height() }); }); - this.area.mousedown(this._onMouseClickHandler.bind(this, "mousedown")); - this.area.mouseup(this._onMouseClickHandler.bind(this, "mouseup")); + this.area.mousedown(this._onMouseClickHandler.bind(this, + EVENT_TYPES.mousedown)); + this.area.mouseup(this._onMouseClickHandler.bind(this, + EVENT_TYPES.mouseup)); this.area.dblclick( - this._onMouseClickHandler.bind(this, "mousedblclick")); + this._onMouseClickHandler.bind(this, EVENT_TYPES.mousedblclick)); this.area.contextmenu(() => false); this.area[0].onmousewheel = event => { - this._sendEvent({ - type: "mousescroll", + this._sendRemoteControlEvent({ + type: EVENT_TYPES.mousescroll, x: event.deltaX, y: event.deltaY }); }; - $(window).keydown(this._onKeyPessHandler.bind(this, "keydown")); - $(window).keyup(this._onKeyPessHandler.bind(this, "keyup")); + $(window).keydown(this._onKeyPessHandler.bind(this, + EVENT_TYPES.keydown)); + $(window).keyup(this._onKeyPessHandler.bind(this, EVENT_TYPES.keyup)); } /** @@ -99,7 +116,7 @@ class Controller { * @param {Event} event the mouse event. */ _onMouseClickHandler(type, event) { - this._sendEvent({ + this._sendRemoteControlEvent({ type: type, button: event.which }); @@ -111,7 +128,7 @@ class Controller { * @param {Event} event the key event. */ _onKeyPessHandler(type, event) { - this._sendEvent({ + this._sendRemoteControlEvent({ type: type, key: getKey(event), modifiers: getModifiers(event), @@ -123,14 +140,13 @@ class Controller { * @param {Object} event the remote control event. */ _sendRemoteControlEvent(event) { + if(!this.enabled) + return; try{ APP.conference.sendEndpointMessage("", - {type: "remote-control-event", event}); + {type: API_EVENT_TYPE, event}); } catch (e) { // failed to send the event. } } } - - -export default new Controller(); diff --git a/modules/remotecontrol/Receiver.js b/modules/remotecontrol/Receiver.js index 144d5b57e..7785d6324 100644 --- a/modules/remotecontrol/Receiver.js +++ b/modules/remotecontrol/Receiver.js @@ -1,4 +1,7 @@ /* global APP, JitsiMeetJS */ +import {DISCO_REMOTE_CONTROL_FEATURE, API_EVENT_TYPE} + from "../../service/remotecontrol/Constants"; + const ConferenceEvents = JitsiMeetJS.events.conference; /** @@ -7,20 +10,36 @@ const ConferenceEvents = JitsiMeetJS.events.conference; * API module. From there the events can be received from wrapper application * and executed. */ -class Receiver { +export default class Receiver { /** * Creates new instance. * @constructor */ - constructor() {} + constructor() { + this.enabled = false; + } + + /** + * Enables / Disables the remote control + * @param {boolean} enabled the new state. + */ + enable(enabled) { + if(this.enabled !== enabled && enabled === true) { + this.enabled = enabled; + // Announce remote control support. + APP.connection.addFeature(DISCO_REMOTE_CONTROL_FEATURE, true); + } + } /** * Attaches listener for ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED events. */ start() { - APP.conference.addConferenceListener( - ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED, - this._onRemoteControlEvent); + if(this.enabled) { + APP.conference.addConferenceListener( + ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED, + this._onRemoteControlEvent); + } } /** @@ -39,9 +58,7 @@ class Receiver { * @param {Object} event the remote control event. */ _onRemoteControlEvent(participant, event) { - if(event.type === "remote-control-event") + if(event.type === API_EVENT_TYPE && this.enabled) APP.API.sendRemoteControlEvent(event.event); } } - -export default new Receiver(); diff --git a/modules/remotecontrol/RemoteControl.js b/modules/remotecontrol/RemoteControl.js new file mode 100644 index 000000000..2da4680dd --- /dev/null +++ b/modules/remotecontrol/RemoteControl.js @@ -0,0 +1,51 @@ +/* global APP, config */ +import Controller from "./Controller"; +import Receiver from "./Receiver"; + +/** + * Implements the remote control functionality. + */ +class RemoteControl { + /** + * Constructs new instance. Creates controller and receiver properties. + * @constructor + */ + constructor() { + this.controller = new Controller(); + this.receiver = new Receiver(); + this.enabled = false; + this.initialized = false; + } + + /** + * Initializes the remote control - checks if the remote control should be + * enabled or not, initializes the API module. + */ + init() { + if(config.disableRemoteControl || this.initialized) { + return; + } + this.initialized = true; + APP.API.init({ + forceEnable: true, + }); + this.controller.enable(true); + } + + /** + * Handles API event for support for executing remote control events into + * the wrapper application. + * @param {boolean} isSupported true if the receiver side is supported by + * the wrapper application. + */ + onRemoteControlSupported(isSupported) { + if(isSupported && !config.disableRemoteControl) { + this.enabled = true; + if(this.initialized) { + this.receiver.enable(true); + } + } + } +} + +export default new RemoteControl(); diff --git a/modules/tokendata/TokenData.js b/modules/tokendata/TokenData.js index 6e1856ddf..86438937b 100644 --- a/modules/tokendata/TokenData.js +++ b/modules/tokendata/TokenData.js @@ -73,10 +73,6 @@ class TokenData{ this.jwt = jwt; - //External API settings - this.externalAPISettings = { - forceEnable: true - }; this._decode(); // Use JWT param as token if there is not other token set and if the // iss field is not anonymous. If you want to pass data with JWT token diff --git a/react/features/app/functions.web.js b/react/features/app/functions.web.js index ec4bbee3a..77319fde8 100644 --- a/react/features/app/functions.web.js +++ b/react/features/app/functions.web.js @@ -23,7 +23,7 @@ export function init() { APP.keyboardshortcut = KeyboardShortcut; APP.tokenData = getTokenData(); - APP.API.init(APP.tokenData.externalAPISettings); + APP.API.init(APP.tokenData.jwt ? {forceEnable: true} : undefined); APP.translation.init(settings.getLanguage()); } diff --git a/service/remotecontrol/Constants.js b/service/remotecontrol/Constants.js new file mode 100644 index 000000000..34e776ea9 --- /dev/null +++ b/service/remotecontrol/Constants.js @@ -0,0 +1,23 @@ +/** + * The value for the "var" attribute of feature tag in disco-info packets. + */ +export const DISCO_REMOTE_CONTROL_FEATURE + = "http://jitsi.org/meet/remotecontrol"; + +/** + * Types of remote-control-event events. + */ +export const EVENT_TYPES = { + mousemove: "mousemove", + mousedown: "mousedown", + mouseup: "mouseup", + mousedblclick: "mousedblclick", + mousescroll: "mousescroll", + keydown: "keydown", + keyup: "keyup" +}; + +/** + * The type of remote control events sent trough the API module. + */ +export const API_EVENT_TYPE = "remote-control-event";