ref: enable/disable microphone button

Make toolbar's microphone button enabled whenever there are any
'audioinput' devices available and allow to add audio during
the conference even if microphone permissions were denied on startup.
This commit is contained in:
paweldomas 2017-07-24 15:56:57 +02:00 committed by yanas
parent bfa5f4c953
commit 68f4a4ae9f
8 changed files with 120 additions and 58 deletions

View File

@ -33,7 +33,10 @@ import { updateDeviceList } from './react/features/base/devices';
import {
isFatalJitsiConnectionError
} from './react/features/base/lib-jitsi-meet';
import { setVideoAvailable } from './react/features/base/media';
import {
setAudioAvailable,
setVideoAvailable
} from './react/features/base/media';
import {
localParticipantConnectionStatusChanged,
localParticipantRoleChanged,
@ -671,24 +674,17 @@ export default {
}
// if user didn't give access to mic or camera or doesn't have
// them at all, we disable corresponding toolbar buttons
// them at all, we mark corresponding toolbar buttons as muted,
// so that the user can try unmute later on and add audio/video
// to the conference
if (!tracks.find((t) => t.isAudioTrack())) {
APP.UI.setMicrophoneButtonEnabled(false);
this.audioMuted = true;
APP.UI.setAudioMuted(this.getMyUserId(), this.audioMuted);
}
if (!tracks.find((t) => t.isVideoTrack())) {
// Instead of disabling the button we want to show button
// muted, so that the user can have the opportunity to add
// the video later on, even if joined without it.
this.videoMuted = true;
APP.UI.setVideoMuted(this.getMyUserId(), this.videoMuted);
// FIXME this is a workaround for the situation where
// both audio and video permissions are rejected initially
// and the callback from _initDeviceList will never be
// executed (GUM not initialized - check lib-jitsi-meet).
// The goal here is to disable the video icon in case no
// video permissions were granted.
this.updateVideoIconEnabled();
}
this._initDeviceList();
@ -1156,7 +1152,6 @@ export default {
this.isSharingScreen = false;
}
APP.UI.setVideoMuted(this.getMyUserId(), this.videoMuted);
this.updateVideoIconEnabled();
APP.UI.updateDesktopSharingButtons();
});
},
@ -1178,7 +1173,6 @@ export default {
} else {
this.audioMuted = false;
}
APP.UI.setMicrophoneButtonEnabled(true);
APP.UI.setAudioMuted(this.getMyUserId(), this.audioMuted);
});
},
@ -1799,7 +1793,19 @@ export default {
APP.UI.updateDTMFSupport(isDTMFSupported);
});
APP.UI.addListener(UIEvents.AUDIO_MUTED, muteLocalAudio);
APP.UI.addListener(UIEvents.AUDIO_MUTED, muted => {
if (!localAudio && this.audioMuted && !muted) {
createLocalTracks({ devices: ['audio'] }, false)
.then(([audioTrack]) => {
this.useAudioStream(audioTrack);
})
.catch(error => {
APP.UI.showDeviceErrorDialog(error, null);
});
} else {
muteLocalAudio(muted);
}
});
APP.UI.addListener(UIEvents.VIDEO_MUTED, muted => {
if (this.isAudioOnly() && !muted) {
this._displayAudioOnlyTooltip('videoMute');
@ -2139,7 +2145,6 @@ export default {
mediaDeviceHelper.setCurrentMediaDevices(devices);
APP.UI.onAvailableDevicesChanged(devices);
APP.store.dispatch(updateDeviceList(devices));
this.updateVideoIconEnabled();
});
this.deviceChangeListener = (devices) =>
@ -2221,9 +2226,31 @@ export default {
.then(() => {
mediaDeviceHelper.setCurrentMediaDevices(devices);
APP.UI.onAvailableDevicesChanged(devices);
this.updateVideoIconEnabled();
});
},
/**
* Determines whether or not the audio button should be enabled.
*/
updateAudioIconEnabled() {
const audioMediaDevices
= mediaDeviceHelper.getCurrentMediaDevices().audioinput;
const audioDeviceCount
= audioMediaDevices ? audioMediaDevices.length : 0;
// The audio functionality is considered available if there are any
// audio devices detected or if the local audio stream already exists.
const available = audioDeviceCount > 0 || Boolean(localAudio);
logger.debug(
'Microphone button enabled: ' + available,
'local audio: ' + localAudio,
'audio devices: ' + audioMediaDevices,
'device count: ' + audioDeviceCount);
APP.store.dispatch(setAudioAvailable(available));
},
/**
* Determines whether or not the video button should be enabled.
*/

View File

@ -32,7 +32,6 @@ import {
import {
checkAutoEnableDesktopSharing,
dockToolbox,
setAudioIconEnabled,
setToolbarButton,
showDialPadButton,
showEtherpadButton,
@ -710,9 +709,7 @@ UI.setAudioMuted = function (id, muted) {
VideoLayout.onAudioMute(id, muted);
if (APP.conference.isLocalId(id)) {
APP.store.dispatch(setAudioMuted(muted));
APP.store.dispatch(setToolbarButton('microphone', {
toggled: muted
}));
APP.conference.updateAudioIconEnabled();
}
};
@ -723,6 +720,7 @@ UI.setVideoMuted = function (id, muted) {
VideoLayout.onVideoMute(id, muted);
if (APP.conference.isLocalId(id)) {
APP.store.dispatch(setVideoMuted(muted));
APP.conference.updateVideoIconEnabled();
}
};
@ -1096,6 +1094,8 @@ UI.onLocalRaiseHandChanged = function (isRaisedHand) {
*/
UI.onAvailableDevicesChanged = function (devices) {
APP.store.dispatch(updateDeviceList(devices));
APP.conference.updateAudioIconEnabled();
APP.conference.updateVideoIconEnabled();
};
/**
@ -1367,15 +1367,6 @@ UI.onSharedVideoStop = function (id, attributes) {
sharedVideoManager.onSharedVideoStop(id, attributes);
};
/**
* Enables / disables microphone toolbar button.
*
* @param {boolean} enabled indicates if the microphone button should be
* enabled or disabled
*/
UI.setMicrophoneButtonEnabled
= enabled => APP.store.dispatch(setAudioIconEnabled(enabled));
/**
* Indicates if any the "top" overlays are currently visible. The check includes
* the call/ring overlay, the suspended overlay, the GUM permissions overlay,

View File

@ -55,11 +55,6 @@ function getNewAudioInputDevice(newDevices, localAudio) {
availableAudioInputDevices[0].label !== '') {
return availableAudioInputDevices[0].deviceId;
}
// Otherwise we assume that we don't have any audio input devices
// to use and that's why disable microphone button on UI.
else {
APP.UI.setMicrophoneButtonEnabled(false);
}
} else {
// And here we handle case when we already have some device working,
// but we plug-in a "preferred" (previously selected in settings, stored

View File

@ -8,6 +8,16 @@
*/
export const SET_AUDIO_MUTED = Symbol('SET_AUDIO_MUTED');
/**
* The type of (redux) action to adjust the availability of the local audio.
*
* {
* type: SET_AUDIO_AVAILABLE,
* muted: boolean
* }
*/
export const SET_AUDIO_AVAILABLE = Symbol('SET_AUDIO_AVAILABLE');
/**
* The type of (redux) action to set the facing mode of the local video camera
* to a specific value.

View File

@ -4,6 +4,7 @@ import type { Dispatch } from 'redux';
import {
SET_AUDIO_MUTED,
SET_AUDIO_AVAILABLE,
SET_CAMERA_FACING_MODE,
SET_VIDEO_AVAILABLE,
SET_VIDEO_MUTED,
@ -11,6 +12,23 @@ import {
} from './actionTypes';
import { CAMERA_FACING_MODE } from './constants';
/**
* Action to adjust the availability of the local audio.
*
* @param {boolean} available - True if the local audio is to be marked as
* available or false if the local audio is not available.
* @returns {{
* type: SET_AUDIO_AVAILABLE,
* available: boolean
* }}
*/
export function setAudioAvailable(available: boolean) {
return {
type: SET_AUDIO_AVAILABLE,
available
};
}
/**
* Action to set the muted state of the local audio.
*

View File

@ -3,6 +3,7 @@ import { combineReducers } from 'redux';
import { ReducerRegistry } from '../redux';
import {
SET_AUDIO_AVAILABLE,
SET_AUDIO_MUTED,
SET_CAMERA_FACING_MODE,
SET_VIDEO_AVAILABLE,
@ -24,6 +25,7 @@ import { CAMERA_FACING_MODE } from './constants';
* @type {AudioMediaState}
*/
const AUDIO_INITIAL_MEDIA_STATE = {
available: true,
muted: false
};
@ -38,6 +40,12 @@ const AUDIO_INITIAL_MEDIA_STATE = {
*/
function _audio(state = AUDIO_INITIAL_MEDIA_STATE, action) {
switch (action.type) {
case SET_AUDIO_AVAILABLE:
return {
...state,
available: action.available
};
case SET_AUDIO_MUTED:
return {
...state,

View File

@ -51,27 +51,6 @@ export function clearToolboxTimeout(): Object {
};
}
/**
* Enables/disables audio toolbar button.
*
* @param {boolean} enabled - True if the button should be enabled; otherwise,
* false.
* @returns {Function}
*/
export function setAudioIconEnabled(enabled: boolean = false): Function {
return (dispatch: Dispatch<*>) => {
const i18nKey = enabled ? 'mute' : 'micDisabled';
const i18n = `[content]toolbar.${i18nKey}`;
const button = {
enabled,
i18n,
toggled: !enabled
};
dispatch(setToolbarButton('microphone', button));
};
}
/**
* Signals that value of conference subject should be changed.
*

View File

@ -1,6 +1,10 @@
/* @flow */
import { SET_VIDEO_AVAILABLE, SET_VIDEO_MUTED } from '../base/media';
import {
SET_AUDIO_AVAILABLE,
SET_AUDIO_MUTED,
SET_VIDEO_AVAILABLE,
SET_VIDEO_MUTED } from '../base/media';
import { MiddlewareRegistry } from '../base/redux';
import { setToolbarButton } from './actions';
@ -33,6 +37,11 @@ MiddlewareRegistry.register(store => next => action => {
break;
}
case SET_AUDIO_AVAILABLE:
case SET_AUDIO_MUTED: {
return _setAudioAvailableOrMuted(store, next, action);
}
case SET_VIDEO_AVAILABLE:
case SET_VIDEO_MUTED:
return _setVideoAvailableOrMuted(store, next, action);
@ -41,6 +50,31 @@ MiddlewareRegistry.register(store => next => action => {
return next(action);
});
/**
* Adjusts the state of toolbar's microphone button.
*
* @param {Store} store - The Redux store instance.
* @param {Function} next - The redux function to continue dispatching the
* specified {@code action} in the specified {@code store}.
* @param {Object} action - Either SET_AUDIO_AVAILABLE or SET_AUDIO_MUTED.
*
* @returns {*}
*/
function _setAudioAvailableOrMuted({ dispatch, getState }, next, action) {
const result = next(action);
const { available, muted } = getState()['features/base/media'].audio;
const i18nKey = available ? 'mute' : 'micDisabled';
dispatch(setToolbarButton('microphone', {
enabled: available,
i18n: `[content]toolbar.${i18nKey}`,
toggled: available ? muted : true
}));
return result;
}
/**
* Adjusts the state of toolbar's camera button.
*