feat(remotecontrol): announce remotecontrol support

This commit is contained in:
hristoterezov 2016-12-20 16:15:13 -06:00
parent 896650d005
commit 0f33e59e4d
9 changed files with 168 additions and 47 deletions

6
app.js
View File

@ -22,8 +22,7 @@ import conference from './conference';
import API from './modules/API/API'; import API from './modules/API/API';
import translation from "./modules/translation/translation"; import translation from "./modules/translation/translation";
// For remote control testing: import remoteControl from "./modules/remotecontrol/remotecontrol";
// import remoteControlController from "./modules/remotecontrol/Controller";
const APP = { const APP = {
// Used by do_external_connect.js if we receive the attach data after // Used by do_external_connect.js if we receive the attach data after
@ -61,7 +60,8 @@ const APP = {
*/ */
ConferenceUrl : null, ConferenceUrl : null,
connection: null, connection: null,
API API,
remoteControl
}; };
// TODO The execution of the mobile app starts from react/index.native.js. // TODO The execution of the mobile app starts from react/index.native.js.

View File

@ -488,6 +488,7 @@ export default {
}).then(([tracks, con]) => { }).then(([tracks, con]) => {
logger.log('initialized with %s local tracks', tracks.length); logger.log('initialized with %s local tracks', tracks.length);
APP.connection = connection = con; APP.connection = connection = con;
APP.remoteControl.init();
this._bindConnectionFailedHandler(con); this._bindConnectionFailedHandler(con);
this._createRoom(tracks); this._createRoom(tracks);
this.isDesktopSharingEnabled = this.isDesktopSharingEnabled =

View File

@ -55,7 +55,9 @@ function initCommands() {
APP.conference.toggleScreenSharing.bind(APP.conference), APP.conference.toggleScreenSharing.bind(APP.conference),
"video-hangup": () => APP.conference.hangup(), "video-hangup": () => APP.conference.hangup(),
"email": APP.conference.changeLocalEmail, "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) { Object.keys(commands).forEach(function (key) {
postis.listen(key, args => commands[key](...args)); 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 * Initializes the APIConnector. Setups message event listeners that will
* receive information from external applications that embed Jitsi Meet. * receive information from external applications that embed Jitsi Meet.
@ -108,6 +116,13 @@ export default {
return; return;
enabled = true; enabled = true;
}
/**
* initializes postis library.
* @private
*/
_initPostis() {
let postisOptions = { let postisOptions = {
window: target window: target
}; };
@ -116,7 +131,7 @@ export default {
= "jitsi_meet_external_api_" + jitsi_meet_external_api_id; = "jitsi_meet_external_api_" + jitsi_meet_external_api_id;
postis = postisInit(postisOptions); postis = postisInit(postisOptions);
initCommands(); initCommands();
}, }
/** /**
* Notify external application (if API is enabled) that message was sent. * Notify external application (if API is enabled) that message was sent.
@ -124,7 +139,7 @@ export default {
*/ */
notifySendingChatMessage (body) { notifySendingChatMessage (body) {
triggerEvent("outgoing-message", {"message": body}); triggerEvent("outgoing-message", {"message": body});
}, }
/** /**
* Notify external application (if API is enabled) that * Notify external application (if API is enabled) that
@ -143,7 +158,7 @@ export default {
"incoming-message", "incoming-message",
{"from": id, "nick": nick, "message": body, "stamp": ts} {"from": id, "nick": nick, "message": body, "stamp": ts}
); );
}, }
/** /**
* Notify external application (if API is enabled) that * Notify external application (if API is enabled) that
@ -152,7 +167,7 @@ export default {
*/ */
notifyUserJoined (id) { notifyUserJoined (id) {
triggerEvent("participant-joined", {id}); triggerEvent("participant-joined", {id});
}, }
/** /**
* Notify external application (if API is enabled) that * Notify external application (if API is enabled) that
@ -161,7 +176,7 @@ export default {
*/ */
notifyUserLeft (id) { notifyUserLeft (id) {
triggerEvent("participant-left", {id}); triggerEvent("participant-left", {id});
}, }
/** /**
* Notify external application (if API is enabled) that * Notify external application (if API is enabled) that
@ -171,7 +186,7 @@ export default {
*/ */
notifyDisplayNameChanged (id, displayName) { notifyDisplayNameChanged (id, displayName) {
triggerEvent("display-name-change", {id, displayname: displayName}); triggerEvent("display-name-change", {id, displayname: displayName});
}, }
/** /**
* Notify external application (if API is enabled) that * Notify external application (if API is enabled) that
@ -181,7 +196,7 @@ export default {
*/ */
notifyConferenceJoined (room) { notifyConferenceJoined (room) {
triggerEvent("video-conference-joined", {roomName: room}); triggerEvent("video-conference-joined", {roomName: room});
}, }
/** /**
* Notify external application (if API is enabled) that * Notify external application (if API is enabled) that
@ -191,7 +206,7 @@ export default {
*/ */
notifyConferenceLeft (room) { notifyConferenceLeft (room) {
triggerEvent("video-conference-left", {roomName: room}); triggerEvent("video-conference-left", {roomName: room});
}, }
/** /**
* Notify external application (if API is enabled) that * Notify external application (if API is enabled) that
@ -199,7 +214,7 @@ export default {
*/ */
notifyReadyToClose () { notifyReadyToClose () {
triggerEvent("video-ready-to-close", {}); triggerEvent("video-ready-to-close", {});
}, }
/** /**
* Sends remote control event. * Sends remote control event.
@ -207,13 +222,15 @@ export default {
*/ */
sendRemoteControlEvent(event) { sendRemoteControlEvent(event) {
sendMessage({method: "remote-control-event", params: event}); sendMessage({method: "remote-control-event", params: event});
}, }
/** /**
* Removes the listeners. * Removes the listeners.
*/ */
dispose: function () { dispose () {
if(enabled) if(enabled)
postis.destroy(); postis.destroy();
} }
}; }
export default new API();

View File

@ -1,5 +1,7 @@
/* global $, APP */ /* global $, APP */
import * as KeyCodes from "../keycode/keycode"; import * as KeyCodes from "../keycode/keycode";
import {EVENT_TYPES, API_EVENT_TYPE}
from "../../service/remotecontrol/Constants";
/** /**
* Extract the keyboard key from the keyboard event. * 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 * It listens for mouse and keyboard events and sends them to the receiver
* party of the remote control session. * party of the remote control session.
*/ */
class Controller { export default class Controller {
/** /**
* Creates new instance. * 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. * Starts processing the mouse and keyboard events.
@ -54,29 +66,34 @@ class Controller {
* attaching the listeners on. * attaching the listeners on.
*/ */
start(area) { start(area) {
if(!this.enabled)
return;
this.area = area; this.area = area;
this.area.mousemove(event => { this.area.mousemove(event => {
const position = this.area.position(); const position = this.area.position();
this._sendEvent({ this._sendRemoteControlEvent({
type: "mousemove", type: EVENT_TYPES.mousemove,
x: (event.pageX - position.left)/this.area.width(), x: (event.pageX - position.left)/this.area.width(),
y: (event.pageY - position.top)/this.area.height() y: (event.pageY - position.top)/this.area.height()
}); });
}); });
this.area.mousedown(this._onMouseClickHandler.bind(this, "mousedown")); this.area.mousedown(this._onMouseClickHandler.bind(this,
this.area.mouseup(this._onMouseClickHandler.bind(this, "mouseup")); EVENT_TYPES.mousedown));
this.area.mouseup(this._onMouseClickHandler.bind(this,
EVENT_TYPES.mouseup));
this.area.dblclick( this.area.dblclick(
this._onMouseClickHandler.bind(this, "mousedblclick")); this._onMouseClickHandler.bind(this, EVENT_TYPES.mousedblclick));
this.area.contextmenu(() => false); this.area.contextmenu(() => false);
this.area[0].onmousewheel = event => { this.area[0].onmousewheel = event => {
this._sendEvent({ this._sendRemoteControlEvent({
type: "mousescroll", type: EVENT_TYPES.mousescroll,
x: event.deltaX, x: event.deltaX,
y: event.deltaY y: event.deltaY
}); });
}; };
$(window).keydown(this._onKeyPessHandler.bind(this, "keydown")); $(window).keydown(this._onKeyPessHandler.bind(this,
$(window).keyup(this._onKeyPessHandler.bind(this, "keyup")); EVENT_TYPES.keydown));
$(window).keyup(this._onKeyPessHandler.bind(this, EVENT_TYPES.keyup));
} }
/** /**
@ -99,7 +116,7 @@ class Controller {
* @param {Event} event the mouse event. * @param {Event} event the mouse event.
*/ */
_onMouseClickHandler(type, event) { _onMouseClickHandler(type, event) {
this._sendEvent({ this._sendRemoteControlEvent({
type: type, type: type,
button: event.which button: event.which
}); });
@ -111,7 +128,7 @@ class Controller {
* @param {Event} event the key event. * @param {Event} event the key event.
*/ */
_onKeyPessHandler(type, event) { _onKeyPessHandler(type, event) {
this._sendEvent({ this._sendRemoteControlEvent({
type: type, type: type,
key: getKey(event), key: getKey(event),
modifiers: getModifiers(event), modifiers: getModifiers(event),
@ -123,14 +140,13 @@ class Controller {
* @param {Object} event the remote control event. * @param {Object} event the remote control event.
*/ */
_sendRemoteControlEvent(event) { _sendRemoteControlEvent(event) {
if(!this.enabled)
return;
try{ try{
APP.conference.sendEndpointMessage("", APP.conference.sendEndpointMessage("",
{type: "remote-control-event", event}); {type: API_EVENT_TYPE, event});
} catch (e) { } catch (e) {
// failed to send the event. // failed to send the event.
} }
} }
} }
export default new Controller();

View File

@ -1,4 +1,7 @@
/* global APP, JitsiMeetJS */ /* global APP, JitsiMeetJS */
import {DISCO_REMOTE_CONTROL_FEATURE, API_EVENT_TYPE}
from "../../service/remotecontrol/Constants";
const ConferenceEvents = JitsiMeetJS.events.conference; const ConferenceEvents = JitsiMeetJS.events.conference;
/** /**
@ -7,21 +10,37 @@ const ConferenceEvents = JitsiMeetJS.events.conference;
* API module. From there the events can be received from wrapper application * API module. From there the events can be received from wrapper application
* and executed. * and executed.
*/ */
class Receiver { export default class Receiver {
/** /**
* Creates new instance. * Creates new instance.
* @constructor * @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. * Attaches listener for ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED events.
*/ */
start() { start() {
if(this.enabled) {
APP.conference.addConferenceListener( APP.conference.addConferenceListener(
ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED, ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
this._onRemoteControlEvent); this._onRemoteControlEvent);
} }
}
/** /**
* Removes the listener for ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED * Removes the listener for ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED
@ -39,9 +58,7 @@ class Receiver {
* @param {Object} event the remote control event. * @param {Object} event the remote control event.
*/ */
_onRemoteControlEvent(participant, event) { _onRemoteControlEvent(participant, event) {
if(event.type === "remote-control-event") if(event.type === API_EVENT_TYPE && this.enabled)
APP.API.sendRemoteControlEvent(event.event); APP.API.sendRemoteControlEvent(event.event);
} }
} }
export default new Receiver();

View File

@ -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();

View File

@ -73,10 +73,6 @@ class TokenData{
this.jwt = jwt; this.jwt = jwt;
//External API settings
this.externalAPISettings = {
forceEnable: true
};
this._decode(); this._decode();
// Use JWT param as token if there is not other token set and if the // 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 // iss field is not anonymous. If you want to pass data with JWT token

View File

@ -23,7 +23,7 @@ export function init() {
APP.keyboardshortcut = KeyboardShortcut; APP.keyboardshortcut = KeyboardShortcut;
APP.tokenData = getTokenData(); APP.tokenData = getTokenData();
APP.API.init(APP.tokenData.externalAPISettings); APP.API.init(APP.tokenData.jwt ? {forceEnable: true} : undefined);
APP.translation.init(settings.getLanguage()); APP.translation.init(settings.getLanguage());
} }

View File

@ -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";