feat(remotecontrol): Implement requesting remote control permissions

This commit is contained in:
hristoterezov 2016-12-27 20:03:52 -06:00
parent c07fe8fa1e
commit 59b79cffe5
6 changed files with 162 additions and 20 deletions

View file

@ -1,5 +1,5 @@
# Electron's version. # 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. # The architecture of Electron, can be ia32 or x64.
export npm_config_arch=x64 export npm_config_arch=x64
export npm_config_target_arch=x64 export npm_config_target_arch=x64

View file

@ -1,3 +1,4 @@
/* global __dirname, process */
//Electron includes //Electron includes
const electron = require("electron"); const electron = require("electron");
const APP = electron.app; const APP = electron.app;

View file

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

View file

@ -1,4 +1,6 @@
let robot = require("robotjs"); 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. * Attaching to the window for debug purposes.
@ -59,6 +61,45 @@ class RemoteControl {
this.started = false; 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. * Starts processing the events.
*/ */
@ -77,12 +118,13 @@ class RemoteControl {
* Executes the passed event. * Executes the passed event.
* @param {Object} event the remote-control-event. * @param {Object} event the remote-control-event.
*/ */
executeRemoteControlEvent(event) { onRemoteControlEvent(event) {
if(!this.started) {
if(!this.started && event.type !== EVENT_TYPES.permissions) {
return; return;
} }
switch(event.type) { switch(event.type) {
case "mousemove": case EVENT_TYPES.mousemove: {
const x = event.x * width, y = event.y * height; const x = event.x * width, y = event.y * height;
if(mouseButtonStatus === "down") { if(mouseButtonStatus === "down") {
robot.dragMouse(x, y); robot.dragMouse(x, y);
@ -90,20 +132,23 @@ class RemoteControl {
robot.moveMouse(x, y); robot.moveMouse(x, y);
} }
break; break;
case "mousedown": }
case "mouseup": case EVENT_TYPES.mousedown:
case EVENT_TYPES.mouseup: {
mouseButtonStatus = MOUSE_ACTIONS_FROM_EVENT_TYPE[event.type]; mouseButtonStatus = MOUSE_ACTIONS_FROM_EVENT_TYPE[event.type];
robot.mouseToggle( robot.mouseToggle(
mouseButtonStatus, mouseButtonStatus,
(event.button ? MOUSE_BUTTONS[event.button] : undefined)); (event.button ? MOUSE_BUTTONS[event.button] : undefined));
break; break;
case "mousedblclick": }
case EVENT_TYPES.mousedblclick: {
robot.mouseClick( robot.mouseClick(
(event.button ? MOUSE_BUTTONS[event.button] : undefined), (event.button ? MOUSE_BUTTONS[event.button] : undefined),
true); true);
break; break;
case "mousescroll": }
//FIXME: implement horizontal scrolling case EVENT_TYPES.mousescroll:{
//FIXME: implement horizontal scrolling
if(event.y !== 0) { if(event.y !== 0) {
robot.scrollMouse( robot.scrollMouse(
Math.abs(event.y), Math.abs(event.y),
@ -111,15 +156,40 @@ class RemoteControl {
); );
} }
break; break;
case "keydown": }
case "keyup": case EVENT_TYPES.keydown:
case EVENT_TYPES.keyup: {
robot.keyToggle(event.key, robot.keyToggle(event.key,
KEY_ACTIONS_FROM_EVENT_TYPE[event.type], event.modifiers); KEY_ACTIONS_FROM_EVENT_TYPE[event.type], event.modifiers);
break; 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: default:
console.error("Unknown event type!"); 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(); module.exports = new RemoteControl();

View file

@ -32,7 +32,7 @@
"postis": "^2.2.0" "postis": "^2.2.0"
}, },
"devDependencies": { "devDependencies": {
"electron": "1.4.7", "electron": "1.4.13",
"eslint": ">=3", "eslint": ">=3",
"eslint-plugin-jsdoc": "*", "eslint-plugin-jsdoc": "*",
"precommit-hook": "3.0.0" "precommit-hook": "3.0.0"

View file

@ -2,6 +2,7 @@ const remoteControl = require("../../modules/remotecontrol");
let postis = require("postis"); let postis = require("postis");
const setupScreenSharingForWindow = require("../../modules/screensharing"); const setupScreenSharingForWindow = require("../../modules/screensharing");
const config = require("../../config.js"); const config = require("../../config.js");
const {dialog} = require('electron').remote;
/** /**
* The postis channel. * The postis channel.
@ -18,16 +19,51 @@ iframe.onload = onload;
document.body.appendChild(iframe); document.body.appendChild(iframe);
/** /**
* Initializes the remote control functionality. * Factory for dialogs.
*/ */
function initRemoteControl() { class DialogFactory {
remoteControl.start(); /**
channel.ready(() => * Creates new instance
channel.listen('remote-control-event', * @constructor
event => remoteControl.executeRemoteControlEvent(event)) */
); 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: * Handles loaded event for iframe:
* Enables screen sharing functionality to the iframe webpage. * Enables screen sharing functionality to the iframe webpage.
@ -35,10 +71,14 @@ function initRemoteControl() {
* Initializes remote control. * Initializes remote control.
*/ */
function onload() { function onload() {
if(loaded) {
return;
}
loaded = true;
setupScreenSharingForWindow(iframe.contentWindow); setupScreenSharingForWindow(iframe.contentWindow);
channel = postis({ channel = postis({
window: iframe.contentWindow, window: iframe.contentWindow,
windowForEventListening: window windowForEventListening: window
}); });
initRemoteControl(); remoteControl.init(channel, dialogFactory);
} }