From 59b79cffe5251c1adaf2fa1a159d55fcc3696298 Mon Sep 17 00:00:00 2001 From: hristoterezov Date: Tue, 27 Dec 2016 20:03:52 -0600 Subject: [PATCH] feat(remotecontrol): Implement requesting remote control permissions --- electron_npm_rebuild | 2 +- main.js | 1 + modules/remotecontrol/constants.js | 31 ++++++++++ modules/remotecontrol/index.js | 90 ++++++++++++++++++++++++++---- package.json | 2 +- windows/jitsi-meet/render.js | 56 ++++++++++++++++--- 6 files changed, 162 insertions(+), 20 deletions(-) create mode 100644 modules/remotecontrol/constants.js diff --git a/electron_npm_rebuild b/electron_npm_rebuild index 53daab9..0b232d2 100755 --- a/electron_npm_rebuild +++ b/electron_npm_rebuild @@ -1,5 +1,5 @@ # Electron's version. -export npm_config_target=1.4.7 +export npm_config_target=1.4.13 # The architecture of Electron, can be ia32 or x64. export npm_config_arch=x64 export npm_config_target_arch=x64 diff --git a/main.js b/main.js index 0a073fb..22e2be8 100644 --- a/main.js +++ b/main.js @@ -1,3 +1,4 @@ +/* global __dirname, process */ //Electron includes const electron = require("electron"); const APP = electron.app; diff --git a/modules/remotecontrol/constants.js b/modules/remotecontrol/constants.js new file mode 100644 index 0000000..165ed5f --- /dev/null +++ b/modules/remotecontrol/constants.js @@ -0,0 +1,31 @@ +module.exports = { + /** + * Types of remote-control-event events. + */ + EVENT_TYPES: { + mousemove: "mousemove", + mousedown: "mousedown", + mouseup: "mouseup", + mousedblclick: "mousedblclick", + mousescroll: "mousescroll", + keydown: "keydown", + keyup: "keyup", + permissions: "permissions", + stop: "stop", + supported: "supported" + }, + + /** + * Actions for the remote control permission events. + */ + PERMISSIONS_ACTIONS: { + request: "request", + grant: "grant", + deny: "deny" + }, + + /** + * The type of remote control events sent trough the API module. + */ + REMOTE_CONTROL_EVENT_TYPE: "remote-control-event" +}; diff --git a/modules/remotecontrol/index.js b/modules/remotecontrol/index.js index eea2ff3..ca7501a 100644 --- a/modules/remotecontrol/index.js +++ b/modules/remotecontrol/index.js @@ -1,4 +1,6 @@ let robot = require("robotjs"); +const constants = require("../../modules/remotecontrol/constants"); +const {EVENT_TYPES, PERMISSIONS_ACTIONS, REMOTE_CONTROL_EVENT_TYPE} = constants; /** * Attaching to the window for debug purposes. @@ -59,6 +61,45 @@ class RemoteControl { this.started = false; } + /** + * Initializes the remote control functionality. + */ + init(channel, windowManager) { + this.windowManager = windowManager; + this.channel = channel; + this.start(); + this.channel.ready(() => { + this.channel.listen(REMOTE_CONTROL_EVENT_TYPE, + event => this.onRemoteControlEvent(event)); + this.sendEvent({type: EVENT_TYPES.supported}); + }); + } + + /** + * Handles permission requests from Jitsi Meet. + * @param {object} userInfo - information about the user that has requested + * permissions: + * @param {string} userInfo.displayName - display name + * @param {string} userInfo.userJID - the JID of the user. + * @param {string} userInfo.userId - the user id (the resource of the JID) + */ + handlePermissionRequest(userInfo) { + this.windowManager.requestRemoteControlPermissions(userInfo) + .then(result => { + this.sendEvent({ + type: EVENT_TYPES.permissions, + action: result ? PERMISSIONS_ACTIONS.grant + : PERMISSIONS_ACTIONS.deny, + userId: userInfo.userId + }); + if(result) { + this.start(); + } + }).catch(e => { + console.error(e); + }); + } + /** * Starts processing the events. */ @@ -77,12 +118,13 @@ class RemoteControl { * Executes the passed event. * @param {Object} event the remote-control-event. */ - executeRemoteControlEvent(event) { - if(!this.started) { + onRemoteControlEvent(event) { + + if(!this.started && event.type !== EVENT_TYPES.permissions) { return; } switch(event.type) { - case "mousemove": + case EVENT_TYPES.mousemove: { const x = event.x * width, y = event.y * height; if(mouseButtonStatus === "down") { robot.dragMouse(x, y); @@ -90,20 +132,23 @@ class RemoteControl { robot.moveMouse(x, y); } break; - case "mousedown": - case "mouseup": + } + case EVENT_TYPES.mousedown: + case EVENT_TYPES.mouseup: { mouseButtonStatus = MOUSE_ACTIONS_FROM_EVENT_TYPE[event.type]; robot.mouseToggle( mouseButtonStatus, (event.button ? MOUSE_BUTTONS[event.button] : undefined)); break; - case "mousedblclick": + } + case EVENT_TYPES.mousedblclick: { robot.mouseClick( (event.button ? MOUSE_BUTTONS[event.button] : undefined), true); break; - case "mousescroll": - //FIXME: implement horizontal scrolling + } + case EVENT_TYPES.mousescroll:{ + //FIXME: implement horizontal scrolling if(event.y !== 0) { robot.scrollMouse( Math.abs(event.y), @@ -111,15 +156,40 @@ class RemoteControl { ); } break; - case "keydown": - case "keyup": + } + case EVENT_TYPES.keydown: + case EVENT_TYPES.keyup: { robot.keyToggle(event.key, KEY_ACTIONS_FROM_EVENT_TYPE[event.type], event.modifiers); break; + } + case EVENT_TYPES.permissions: { + if(event.action !== PERMISSIONS_ACTIONS.request) + break; + + //Open Dialog and answer + this.handlePermissionRequest({ + userId: event.userId, + userJID: event.userJID, + displayName: event.displayName}); + break; + } + case EVENT_TYPES.stop: { + this.stop(); + break; + } default: console.error("Unknown event type!"); } } + + /** + * Sends remote control event to the controlled participant. + * @param {Object} event the remote control event. + */ + sendEvent(event) { + this.channel.send({method: REMOTE_CONTROL_EVENT_TYPE, params: event}); + } } module.exports = new RemoteControl(); diff --git a/package.json b/package.json index 230d4bd..d2129fa 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "postis": "^2.2.0" }, "devDependencies": { - "electron": "1.4.7", + "electron": "1.4.13", "eslint": ">=3", "eslint-plugin-jsdoc": "*", "precommit-hook": "3.0.0" diff --git a/windows/jitsi-meet/render.js b/windows/jitsi-meet/render.js index c789974..786414e 100644 --- a/windows/jitsi-meet/render.js +++ b/windows/jitsi-meet/render.js @@ -2,6 +2,7 @@ const remoteControl = require("../../modules/remotecontrol"); let postis = require("postis"); const setupScreenSharingForWindow = require("../../modules/screensharing"); const config = require("../../config.js"); +const {dialog} = require('electron').remote; /** * The postis channel. @@ -18,16 +19,51 @@ iframe.onload = onload; document.body.appendChild(iframe); /** - * Initializes the remote control functionality. + * Factory for dialogs. */ -function initRemoteControl() { - remoteControl.start(); - channel.ready(() => - channel.listen('remote-control-event', - event => remoteControl.executeRemoteControlEvent(event)) - ); +class DialogFactory { + /** + * Creates new instance + * @constructor + */ + constructor() { } + + /** + * Shows message box dialog for request for remote control permissions + * @param {object} userInfo - information about the user that has sent the + * request: + * @param {string} userInfo.displayName - display name + * @param {string} userInfo.userJID - the JID of the user. + */ + requestRemoteControlPermissions(userInfo) { + return new Promise( resolve => + dialog.showMessageBox({ + type: "question", + buttons: ["Yes", "No"], + defaultId: 0, + title: "Request for permission for remote control", + message: "Would you like to allow " + userInfo.displayName + + " to remotely control your desktop.", + detail: "userId: " + userInfo.userJID, + cancelId: 1 + }, response => resolve(response === 0? true : false)) + ); + } } +/** + * Dialog factory instance. + */ +const dialogFactory = new DialogFactory(); + +/** + * Boolean variable that indicates whether the onloaded function was already + * called. + * NOTE: Used to not call the onload method more than once during reloads of + * the iframe or location changes. + */ +let loaded = false; + /** * Handles loaded event for iframe: * Enables screen sharing functionality to the iframe webpage. @@ -35,10 +71,14 @@ function initRemoteControl() { * Initializes remote control. */ function onload() { + if(loaded) { + return; + } + loaded = true; setupScreenSharingForWindow(iframe.contentWindow); channel = postis({ window: iframe.contentWindow, windowForEventListening: window }); - initRemoteControl(); + remoteControl.init(channel, dialogFactory); }