2017-06-04 03:12:04 +00:00
|
|
|
import { API_ID } from '../../../modules/API/constants';
|
|
|
|
import {
|
2018-04-12 19:58:20 +00:00
|
|
|
PostMessageTransportBackend,
|
|
|
|
Transport
|
|
|
|
} from '../../../modules/transport';
|
|
|
|
|
2018-08-06 15:24:59 +00:00
|
|
|
import { createDeviceChangedEvent, sendAnalytics } from '../analytics';
|
2018-04-12 19:58:20 +00:00
|
|
|
import {
|
|
|
|
getAudioOutputDeviceId,
|
2017-06-04 03:12:04 +00:00
|
|
|
setAudioInputDevice,
|
2018-08-06 15:24:59 +00:00
|
|
|
setAudioOutputDeviceId,
|
2017-06-04 03:12:04 +00:00
|
|
|
setVideoInputDevice
|
|
|
|
} from '../base/devices';
|
|
|
|
import { i18next } from '../base/i18n';
|
2018-04-12 19:58:20 +00:00
|
|
|
import JitsiMeetJS from '../base/lib-jitsi-meet';
|
2018-08-06 15:24:59 +00:00
|
|
|
import { updateSettings } from '../base/settings';
|
2017-04-09 21:20:37 +00:00
|
|
|
|
2017-06-04 03:12:04 +00:00
|
|
|
import { SET_DEVICE_SELECTION_POPUP_DATA } from './actionTypes';
|
2018-06-20 20:19:53 +00:00
|
|
|
import { getDeviceSelectionDialogProps } from './functions';
|
2017-06-04 03:12:04 +00:00
|
|
|
|
2018-08-06 15:24:59 +00:00
|
|
|
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
|
|
|
|
2017-06-04 03:12:04 +00:00
|
|
|
/**
|
|
|
|
* Opens a popup window with the device selection dialog in it.
|
|
|
|
*
|
|
|
|
* @returns {Function}
|
|
|
|
*/
|
2018-06-20 20:19:53 +00:00
|
|
|
export function openDeviceSelectionPopup() {
|
2017-06-04 03:12:04 +00:00
|
|
|
return (dispatch, getState) => {
|
|
|
|
const { popupDialogData } = getState()['features/device-selection'];
|
|
|
|
|
|
|
|
if (popupDialogData) {
|
|
|
|
popupDialogData.popup.focus();
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// API_ID will always be defined because the iframe api is enabled
|
|
|
|
const scope = `dialog_${API_ID}`;
|
2017-06-09 18:16:03 +00:00
|
|
|
const url = `${
|
|
|
|
window.location.origin}/static/deviceSelectionPopup.html#scope=${
|
2017-10-02 23:08:07 +00:00
|
|
|
encodeURIComponent(JSON.stringify(scope))}`;
|
2017-06-04 03:12:04 +00:00
|
|
|
const popup
|
|
|
|
= window.open(
|
|
|
|
url,
|
|
|
|
'device-selection-popup',
|
|
|
|
'toolbar=no,scrollbars=no,resizable=no,width=720,height=458');
|
|
|
|
|
|
|
|
popup.addEventListener('DOMContentLoaded', () => {
|
|
|
|
popup.init(i18next);
|
|
|
|
});
|
|
|
|
|
|
|
|
const transport = new Transport({
|
|
|
|
backend: new PostMessageTransportBackend({
|
|
|
|
postisOptions: {
|
|
|
|
scope,
|
|
|
|
window: popup
|
|
|
|
}
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
transport.on('request',
|
|
|
|
_processRequest.bind(undefined, dispatch, getState));
|
|
|
|
transport.on('event', event => {
|
|
|
|
if (event.type === 'devices-dialog' && event.name === 'close') {
|
|
|
|
popup.close();
|
|
|
|
transport.dispose();
|
|
|
|
dispatch(_setDeviceSelectionPopupData());
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
|
|
|
|
dispatch(_setDeviceSelectionPopupData({
|
|
|
|
popup,
|
|
|
|
transport
|
|
|
|
}));
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Processes device requests from external applications.
|
|
|
|
*
|
|
|
|
* @param {Dispatch} dispatch - The redux {@code dispatch} function.
|
|
|
|
* @param {Function} getState - The redux function that gets/retrieves the redux
|
|
|
|
* state.
|
|
|
|
* @param {Object} request - The request to be processed.
|
|
|
|
* @param {Function} responseCallback - The callback that will send the
|
|
|
|
* response.
|
|
|
|
* @returns {boolean}
|
2017-11-16 18:26:14 +00:00
|
|
|
*/
|
|
|
|
function _processRequest(dispatch, getState, request, responseCallback) { // eslint-disable-line max-len, max-params
|
2017-06-04 03:12:04 +00:00
|
|
|
if (request.type === 'devices') {
|
2018-04-12 19:58:20 +00:00
|
|
|
const state = getState();
|
|
|
|
const settings = state['features/base/settings'];
|
|
|
|
|
2017-06-04 03:12:04 +00:00
|
|
|
switch (request.name) {
|
|
|
|
case 'isDeviceListAvailable':
|
core: refactor routing
Unfortunately, as the Jitsi Meet development evolved the routing mechanism
became more complex and thre logic ended up spread across multiple parts of the
codebase, which made it hard to follow and extend.
This change aims to fix that by rewriting the routing logic and centralizing it
in (pretty much) a single place, with no implicit inter-dependencies.
In order to arrive there, however, some extra changes were needed, which were
not caught early enough and are thus part of this change:
- JitsiMeetJS initialization is now synchronous: there is nothing async about
it, and the only async requirement (Temasys support) was lifted. See [0].
- WebRTC support can be detected early: building on top of the above, WebRTC
support can now be detected immediately, so take advantage of this to simplify
how we handle unsupported browsers. See [0].
The new router takes decissions based on the Redux state at the time of
invocation. A route can be represented by either a component or a URl reference,
with the latter taking precedence. On mobile, obviously, there is no concept of
URL reference so routing is based solely on components.
[0]: https://github.com/jitsi/lib-jitsi-meet/pull/779
2018-06-29 07:58:31 +00:00
|
|
|
responseCallback(JitsiMeetJS.mediaDevices.isDeviceListAvailable());
|
2017-06-04 03:12:04 +00:00
|
|
|
break;
|
|
|
|
case 'isDeviceChangeAvailable':
|
|
|
|
responseCallback(
|
2017-06-13 00:12:29 +00:00
|
|
|
JitsiMeetJS.mediaDevices.isDeviceChangeAvailable(
|
|
|
|
request.deviceType));
|
2017-06-04 03:12:04 +00:00
|
|
|
break;
|
|
|
|
case 'isMultipleAudioInputSupported':
|
|
|
|
responseCallback(JitsiMeetJS.isMultipleAudioInputSupported());
|
|
|
|
break;
|
|
|
|
case 'getCurrentDevices':
|
|
|
|
responseCallback({
|
2018-04-12 19:58:20 +00:00
|
|
|
audioInput: settings.micDeviceId,
|
|
|
|
audioOutput: getAudioOutputDeviceId(),
|
|
|
|
videoInput: settings.cameraDeviceId
|
2017-06-04 03:12:04 +00:00
|
|
|
});
|
|
|
|
break;
|
|
|
|
case 'getAvailableDevices':
|
|
|
|
responseCallback(getState()['features/base/devices']);
|
|
|
|
break;
|
|
|
|
case 'setDevice': {
|
|
|
|
const { device } = request;
|
|
|
|
|
|
|
|
switch (device.kind) {
|
|
|
|
case 'audioinput':
|
2018-08-06 15:24:59 +00:00
|
|
|
dispatch(setAudioInputDevice(device.id));
|
2017-06-04 03:12:04 +00:00
|
|
|
break;
|
|
|
|
case 'audiooutput':
|
2018-08-06 15:24:59 +00:00
|
|
|
setAudioOutputDeviceId(device.id, dispatch);
|
2017-06-04 03:12:04 +00:00
|
|
|
break;
|
|
|
|
case 'videoinput':
|
2018-08-06 15:24:59 +00:00
|
|
|
dispatch(setVideoInputDevice(device.id));
|
2017-06-04 03:12:04 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
|
|
|
|
}
|
2018-08-06 15:24:59 +00:00
|
|
|
|
2017-06-04 03:12:04 +00:00
|
|
|
responseCallback(true);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets information about device selection popup in the store.
|
|
|
|
*
|
|
|
|
* @param {Object} popupDialogData - Information about the popup.
|
|
|
|
* @param {Object} popupDialog.popup - The popup object returned from
|
|
|
|
* window.open.
|
|
|
|
* @param {Object} popupDialogData.transport - The transport instance used for
|
|
|
|
* communication with the popup window.
|
|
|
|
* @returns {{
|
|
|
|
* type: SET_DEVICE_SELECTION_POPUP_DATA,
|
|
|
|
* popupDialogData: Object
|
|
|
|
* }}
|
|
|
|
*/
|
|
|
|
function _setDeviceSelectionPopupData(popupDialogData) {
|
|
|
|
return {
|
|
|
|
type: SET_DEVICE_SELECTION_POPUP_DATA,
|
|
|
|
popupDialogData
|
2017-04-09 21:20:37 +00:00
|
|
|
};
|
|
|
|
}
|
2018-06-20 20:19:53 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Submits the settings related to device selection.
|
|
|
|
*
|
|
|
|
* @param {Object} newState - The new settings.
|
|
|
|
* @returns {Function}
|
|
|
|
*/
|
|
|
|
export function submitDeviceSelectionTab(newState) {
|
|
|
|
return (dispatch, getState) => {
|
|
|
|
const currentState = getDeviceSelectionDialogProps(getState());
|
|
|
|
|
|
|
|
if (newState.selectedVideoInputId
|
|
|
|
&& newState.selectedVideoInputId
|
|
|
|
!== currentState.selectedVideoInputId) {
|
2018-08-06 15:24:59 +00:00
|
|
|
dispatch(updateSettings({
|
|
|
|
cameraDeviceId: newState.selectedVideoInputId
|
|
|
|
}));
|
|
|
|
|
2018-06-20 20:19:53 +00:00
|
|
|
dispatch(
|
|
|
|
setVideoInputDevice(newState.selectedVideoInputId));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newState.selectedAudioInputId
|
|
|
|
&& newState.selectedAudioInputId
|
|
|
|
!== currentState.selectedAudioInputId) {
|
2018-08-06 15:24:59 +00:00
|
|
|
dispatch(updateSettings({
|
|
|
|
micDeviceId: newState.selectedAudioInputId
|
|
|
|
}));
|
|
|
|
|
2018-06-20 20:19:53 +00:00
|
|
|
dispatch(
|
|
|
|
setAudioInputDevice(newState.selectedAudioInputId));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newState.selectedAudioOutputId
|
|
|
|
&& newState.selectedAudioOutputId
|
|
|
|
!== currentState.selectedAudioOutputId) {
|
2018-08-06 15:24:59 +00:00
|
|
|
sendAnalytics(createDeviceChangedEvent('audio', 'output'));
|
|
|
|
|
|
|
|
setAudioOutputDeviceId(
|
|
|
|
newState.selectedAudioOutputId,
|
|
|
|
dispatch)
|
|
|
|
.then(() => logger.log('changed audio output device'))
|
|
|
|
.catch(err => {
|
|
|
|
logger.warn(
|
|
|
|
'Failed to change audio output device.',
|
|
|
|
'Default or previously set audio output device will',
|
|
|
|
' be used instead.',
|
|
|
|
err);
|
|
|
|
});
|
2018-06-20 20:19:53 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|