You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Receiver.js 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. /* global APP, config, interfaceConfig, JitsiMeetJS */
  2. import * as JitsiMeetConferenceEvents from '../../ConferenceEvents';
  3. import {
  4. DISCO_REMOTE_CONTROL_FEATURE,
  5. EVENT_TYPES,
  6. PERMISSIONS_ACTIONS,
  7. REMOTE_CONTROL_EVENT_TYPE
  8. } from "../../service/remotecontrol/Constants";
  9. import { transport } from '../transport';
  10. import RemoteControlParticipant from "./RemoteControlParticipant";
  11. const ConferenceEvents = JitsiMeetJS.events.conference;
  12. const logger = require("jitsi-meet-logger").getLogger(__filename);
  13. /**
  14. * This class represents the receiver party for a remote controller session.
  15. * It handles "remote-control-event" events and sends them to the
  16. * API module. From there the events can be received from wrapper application
  17. * and executed.
  18. */
  19. export default class Receiver extends RemoteControlParticipant {
  20. /**
  21. * Creates new instance.
  22. * @constructor
  23. */
  24. constructor() {
  25. super();
  26. this.controller = null;
  27. this._remoteControlEventsListener
  28. = this._onRemoteControlEvent.bind(this);
  29. this._userLeftListener = this._onUserLeft.bind(this);
  30. this._hangupListener = this._onHangup.bind(this);
  31. // We expect here that even if we receive the supported event earlier
  32. // it will be cached and we'll receive it.
  33. transport.on('event', ({ event, name }) => {
  34. if(name === REMOTE_CONTROL_EVENT_TYPE) {
  35. this._onRemoteControlAPIEvent(event);
  36. return true;
  37. }
  38. return false;
  39. });
  40. }
  41. /**
  42. * Enables / Disables the remote control
  43. * @param {boolean} enabled the new state.
  44. */
  45. _enable(enabled) {
  46. if(this.enabled === enabled) {
  47. return;
  48. }
  49. this.enabled = enabled;
  50. if(enabled === true) {
  51. logger.log("Remote control receiver enabled.");
  52. // Announce remote control support.
  53. APP.connection.addFeature(DISCO_REMOTE_CONTROL_FEATURE, true);
  54. APP.conference.addConferenceListener(
  55. ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
  56. this._remoteControlEventsListener);
  57. APP.conference.addListener(JitsiMeetConferenceEvents.BEFORE_HANGUP,
  58. this._hangupListener);
  59. } else {
  60. logger.log("Remote control receiver disabled.");
  61. this._stop(true);
  62. APP.connection.removeFeature(DISCO_REMOTE_CONTROL_FEATURE);
  63. APP.conference.removeConferenceListener(
  64. ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
  65. this._remoteControlEventsListener);
  66. APP.conference.removeListener(
  67. JitsiMeetConferenceEvents.BEFORE_HANGUP,
  68. this._hangupListener);
  69. }
  70. }
  71. /**
  72. * Removes the listener for ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED
  73. * events. Sends stop message to the wrapper application. Optionally
  74. * displays dialog for informing the user that remote control session
  75. * ended.
  76. * @param {boolean} dontShowDialog - if true the dialog won't be displayed.
  77. */
  78. _stop(dontShowDialog = false) {
  79. if(!this.controller) {
  80. return;
  81. }
  82. logger.log("Remote control receiver stop.");
  83. this.controller = null;
  84. APP.conference.removeConferenceListener(ConferenceEvents.USER_LEFT,
  85. this._userLeftListener);
  86. transport.sendEvent({
  87. name: REMOTE_CONTROL_EVENT_TYPE,
  88. event: {
  89. type: EVENT_TYPES.stop
  90. }
  91. });
  92. if(!dontShowDialog) {
  93. APP.UI.messageHandler.openMessageDialog(
  94. "dialog.remoteControlTitle",
  95. "dialog.remoteControlStopMessage"
  96. );
  97. }
  98. }
  99. /**
  100. * Calls this._stop() and sends stop message to the controller participant
  101. */
  102. stop() {
  103. if(!this.controller) {
  104. return;
  105. }
  106. this._sendRemoteControlEvent(this.controller, {
  107. type: EVENT_TYPES.stop
  108. });
  109. this._stop();
  110. }
  111. /**
  112. * Listens for data channel EndpointMessage events. Handles only events of
  113. * type remote control. Sends "remote-control-event" events to the API
  114. * module.
  115. * @param {JitsiParticipant} participant the controller participant
  116. * @param {Object} event EndpointMessage event from the data channels.
  117. * @property {string} type property. The function process only events of
  118. * type REMOTE_CONTROL_EVENT_TYPE
  119. * @property {RemoteControlEvent} event - the remote control event.
  120. */
  121. _onRemoteControlEvent(participant, event) {
  122. if(this.enabled && event.type === REMOTE_CONTROL_EVENT_TYPE) {
  123. const remoteControlEvent = event.event;
  124. if(this.controller === null
  125. && remoteControlEvent.type === EVENT_TYPES.permissions
  126. && remoteControlEvent.action === PERMISSIONS_ACTIONS.request) {
  127. // FIXME: Maybe use transport.sendRequest in this case???
  128. remoteControlEvent.userId = participant.getId();
  129. remoteControlEvent.userJID = participant.getJid();
  130. remoteControlEvent.displayName = participant.getDisplayName()
  131. || interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME;
  132. remoteControlEvent.screenSharing
  133. = APP.conference.isSharingScreen;
  134. } else if(this.controller !== participant.getId()) {
  135. return;
  136. } else if(remoteControlEvent.type === EVENT_TYPES.stop) {
  137. this._stop();
  138. return;
  139. }
  140. transport.sendEvent({
  141. name: REMOTE_CONTROL_EVENT_TYPE,
  142. event: remoteControlEvent
  143. });
  144. } else if(event.type === REMOTE_CONTROL_EVENT_TYPE) {
  145. logger.log("Remote control event is ignored because remote "
  146. + "control is disabled", event);
  147. }
  148. }
  149. /**
  150. * Handles remote control permission events.
  151. * @param {String} userId the user id of the participant related to the
  152. * event.
  153. * @param {PERMISSIONS_ACTIONS} action the action related to the event.
  154. */
  155. _onRemoteControlPermissionsEvent(userId, action) {
  156. if(action === PERMISSIONS_ACTIONS.grant) {
  157. APP.conference.addConferenceListener(ConferenceEvents.USER_LEFT,
  158. this._userLeftListener);
  159. this.controller = userId;
  160. logger.log("Remote control permissions granted to: " + userId);
  161. if(!APP.conference.isSharingScreen) {
  162. APP.conference.toggleScreenSharing();
  163. APP.conference.screenSharingPromise.then(() => {
  164. if(APP.conference.isSharingScreen) {
  165. this._sendRemoteControlEvent(userId, {
  166. type: EVENT_TYPES.permissions,
  167. action: action
  168. });
  169. } else {
  170. this._sendRemoteControlEvent(userId, {
  171. type: EVENT_TYPES.permissions,
  172. action: PERMISSIONS_ACTIONS.error
  173. });
  174. }
  175. }).catch(() => {
  176. this._sendRemoteControlEvent(userId, {
  177. type: EVENT_TYPES.permissions,
  178. action: PERMISSIONS_ACTIONS.error
  179. });
  180. });
  181. return;
  182. }
  183. }
  184. this._sendRemoteControlEvent(userId, {
  185. type: EVENT_TYPES.permissions,
  186. action
  187. });
  188. }
  189. /**
  190. * Handles remote control events from the external app. Currently only
  191. * events with type = EVENT_TYPES.supported or EVENT_TYPES.permissions
  192. * @param {RemoteControlEvent} event the remote control event.
  193. */
  194. _onRemoteControlAPIEvent(event) {
  195. switch(event.type) {
  196. case EVENT_TYPES.supported:
  197. this._onRemoteControlSupported();
  198. break;
  199. case EVENT_TYPES.permissions:
  200. this._onRemoteControlPermissionsEvent(event.userId, event.action);
  201. break;
  202. }
  203. }
  204. /**
  205. * Handles events for support for executing remote control events into
  206. * the wrapper application.
  207. */
  208. _onRemoteControlSupported() {
  209. logger.log("Remote Control supported.");
  210. if(!config.disableRemoteControl) {
  211. this._enable(true);
  212. } else {
  213. logger.log("Remote Control disabled.");
  214. }
  215. }
  216. /**
  217. * Calls the stop method if the other side have left.
  218. * @param {string} id - the user id for the participant that have left
  219. */
  220. _onUserLeft(id) {
  221. if(this.controller === id) {
  222. this._stop();
  223. }
  224. }
  225. /**
  226. * Handles hangup events. Disables the receiver.
  227. */
  228. _onHangup() {
  229. this._enable(false);
  230. }
  231. }