Browse Source

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.
j8
paweldomas 8 years ago
parent
commit
68f4a4ae9f

+ 45
- 18
conference.js View File

33
 import {
33
 import {
34
     isFatalJitsiConnectionError
34
     isFatalJitsiConnectionError
35
 } from './react/features/base/lib-jitsi-meet';
35
 } from './react/features/base/lib-jitsi-meet';
36
-import { setVideoAvailable } from './react/features/base/media';
36
+import {
37
+    setAudioAvailable,
38
+    setVideoAvailable
39
+} from './react/features/base/media';
37
 import {
40
 import {
38
     localParticipantConnectionStatusChanged,
41
     localParticipantConnectionStatusChanged,
39
     localParticipantRoleChanged,
42
     localParticipantRoleChanged,
671
                 }
674
                 }
672
 
675
 
673
                 // if user didn't give access to mic or camera or doesn't have
676
                 // if user didn't give access to mic or camera or doesn't have
674
-                // them at all, we disable corresponding toolbar buttons
677
+                // them at all, we mark corresponding toolbar buttons as muted,
678
+                // so that the user can try unmute later on and add audio/video
679
+                // to the conference
675
                 if (!tracks.find((t) => t.isAudioTrack())) {
680
                 if (!tracks.find((t) => t.isAudioTrack())) {
676
-                    APP.UI.setMicrophoneButtonEnabled(false);
681
+                    this.audioMuted = true;
682
+                    APP.UI.setAudioMuted(this.getMyUserId(), this.audioMuted);
677
                 }
683
                 }
678
 
684
 
679
                 if (!tracks.find((t) => t.isVideoTrack())) {
685
                 if (!tracks.find((t) => t.isVideoTrack())) {
680
-                    // Instead of disabling the button we want to show button
681
-                    // muted, so that the user can have the opportunity to add
682
-                    // the video later on, even if joined without it.
683
                     this.videoMuted = true;
686
                     this.videoMuted = true;
684
                     APP.UI.setVideoMuted(this.getMyUserId(), this.videoMuted);
687
                     APP.UI.setVideoMuted(this.getMyUserId(), this.videoMuted);
685
-                    // FIXME this is a workaround for the situation where
686
-                    // both audio and video permissions are rejected initially
687
-                    // and the callback from _initDeviceList will never be
688
-                    // executed (GUM not initialized - check lib-jitsi-meet).
689
-                    // The goal here is to disable the video icon in case no
690
-                    // video permissions were granted.
691
-                    this.updateVideoIconEnabled();
692
                 }
688
                 }
693
 
689
 
694
                 this._initDeviceList();
690
                 this._initDeviceList();
1156
                     this.isSharingScreen = false;
1152
                     this.isSharingScreen = false;
1157
                 }
1153
                 }
1158
                 APP.UI.setVideoMuted(this.getMyUserId(), this.videoMuted);
1154
                 APP.UI.setVideoMuted(this.getMyUserId(), this.videoMuted);
1159
-                this.updateVideoIconEnabled();
1160
                 APP.UI.updateDesktopSharingButtons();
1155
                 APP.UI.updateDesktopSharingButtons();
1161
             });
1156
             });
1162
     },
1157
     },
1178
                 } else {
1173
                 } else {
1179
                     this.audioMuted = false;
1174
                     this.audioMuted = false;
1180
                 }
1175
                 }
1181
-                APP.UI.setMicrophoneButtonEnabled(true);
1182
                 APP.UI.setAudioMuted(this.getMyUserId(), this.audioMuted);
1176
                 APP.UI.setAudioMuted(this.getMyUserId(), this.audioMuted);
1183
             });
1177
             });
1184
     },
1178
     },
1799
             APP.UI.updateDTMFSupport(isDTMFSupported);
1793
             APP.UI.updateDTMFSupport(isDTMFSupported);
1800
         });
1794
         });
1801
 
1795
 
1802
-        APP.UI.addListener(UIEvents.AUDIO_MUTED, muteLocalAudio);
1796
+        APP.UI.addListener(UIEvents.AUDIO_MUTED, muted => {
1797
+            if (!localAudio && this.audioMuted && !muted) {
1798
+                createLocalTracks({ devices: ['audio'] }, false)
1799
+                    .then(([audioTrack]) => {
1800
+                        this.useAudioStream(audioTrack);
1801
+                    })
1802
+                    .catch(error => {
1803
+                        APP.UI.showDeviceErrorDialog(error, null);
1804
+                    });
1805
+            } else {
1806
+                muteLocalAudio(muted);
1807
+            }
1808
+        });
1803
         APP.UI.addListener(UIEvents.VIDEO_MUTED, muted => {
1809
         APP.UI.addListener(UIEvents.VIDEO_MUTED, muted => {
1804
             if (this.isAudioOnly() && !muted) {
1810
             if (this.isAudioOnly() && !muted) {
1805
                 this._displayAudioOnlyTooltip('videoMute');
1811
                 this._displayAudioOnlyTooltip('videoMute');
2139
                         mediaDeviceHelper.setCurrentMediaDevices(devices);
2145
                         mediaDeviceHelper.setCurrentMediaDevices(devices);
2140
                         APP.UI.onAvailableDevicesChanged(devices);
2146
                         APP.UI.onAvailableDevicesChanged(devices);
2141
                         APP.store.dispatch(updateDeviceList(devices));
2147
                         APP.store.dispatch(updateDeviceList(devices));
2142
-                        this.updateVideoIconEnabled();
2143
                     });
2148
                     });
2144
 
2149
 
2145
                     this.deviceChangeListener = (devices) =>
2150
                     this.deviceChangeListener = (devices) =>
2221
             .then(() => {
2226
             .then(() => {
2222
                 mediaDeviceHelper.setCurrentMediaDevices(devices);
2227
                 mediaDeviceHelper.setCurrentMediaDevices(devices);
2223
                 APP.UI.onAvailableDevicesChanged(devices);
2228
                 APP.UI.onAvailableDevicesChanged(devices);
2224
-                this.updateVideoIconEnabled();
2225
             });
2229
             });
2226
     },
2230
     },
2231
+
2232
+    /**
2233
+     * Determines whether or not the audio button should be enabled.
2234
+     */
2235
+    updateAudioIconEnabled() {
2236
+        const audioMediaDevices
2237
+            = mediaDeviceHelper.getCurrentMediaDevices().audioinput;
2238
+        const audioDeviceCount
2239
+            = audioMediaDevices ? audioMediaDevices.length : 0;
2240
+
2241
+        // The audio functionality is considered available if there are any
2242
+        // audio devices detected or if the local audio stream already exists.
2243
+        const available = audioDeviceCount > 0 || Boolean(localAudio);
2244
+
2245
+        logger.debug(
2246
+            'Microphone button enabled: ' + available,
2247
+            'local audio: ' + localAudio,
2248
+            'audio devices: ' + audioMediaDevices,
2249
+            'device count: ' + audioDeviceCount);
2250
+
2251
+        APP.store.dispatch(setAudioAvailable(available));
2252
+    },
2253
+
2227
     /**
2254
     /**
2228
      * Determines whether or not the video button should be enabled.
2255
      * Determines whether or not the video button should be enabled.
2229
      */
2256
      */

+ 4
- 13
modules/UI/UI.js View File

32
 import {
32
 import {
33
     checkAutoEnableDesktopSharing,
33
     checkAutoEnableDesktopSharing,
34
     dockToolbox,
34
     dockToolbox,
35
-    setAudioIconEnabled,
36
     setToolbarButton,
35
     setToolbarButton,
37
     showDialPadButton,
36
     showDialPadButton,
38
     showEtherpadButton,
37
     showEtherpadButton,
710
     VideoLayout.onAudioMute(id, muted);
709
     VideoLayout.onAudioMute(id, muted);
711
     if (APP.conference.isLocalId(id)) {
710
     if (APP.conference.isLocalId(id)) {
712
         APP.store.dispatch(setAudioMuted(muted));
711
         APP.store.dispatch(setAudioMuted(muted));
713
-        APP.store.dispatch(setToolbarButton('microphone', {
714
-            toggled: muted
715
-        }));
712
+        APP.conference.updateAudioIconEnabled();
716
     }
713
     }
717
 };
714
 };
718
 
715
 
723
     VideoLayout.onVideoMute(id, muted);
720
     VideoLayout.onVideoMute(id, muted);
724
     if (APP.conference.isLocalId(id)) {
721
     if (APP.conference.isLocalId(id)) {
725
         APP.store.dispatch(setVideoMuted(muted));
722
         APP.store.dispatch(setVideoMuted(muted));
723
+        APP.conference.updateVideoIconEnabled();
726
     }
724
     }
727
 };
725
 };
728
 
726
 
1096
  */
1094
  */
1097
 UI.onAvailableDevicesChanged = function (devices) {
1095
 UI.onAvailableDevicesChanged = function (devices) {
1098
     APP.store.dispatch(updateDeviceList(devices));
1096
     APP.store.dispatch(updateDeviceList(devices));
1097
+    APP.conference.updateAudioIconEnabled();
1098
+    APP.conference.updateVideoIconEnabled();
1099
 };
1099
 };
1100
 
1100
 
1101
 /**
1101
 /**
1367
         sharedVideoManager.onSharedVideoStop(id, attributes);
1367
         sharedVideoManager.onSharedVideoStop(id, attributes);
1368
 };
1368
 };
1369
 
1369
 
1370
-/**
1371
- * Enables / disables microphone toolbar button.
1372
- *
1373
- * @param {boolean} enabled indicates if the microphone button should be
1374
- * enabled or disabled
1375
- */
1376
-UI.setMicrophoneButtonEnabled
1377
-    = enabled => APP.store.dispatch(setAudioIconEnabled(enabled));
1378
-
1379
 /**
1370
 /**
1380
  * Indicates if any the "top" overlays are currently visible. The check includes
1371
  * Indicates if any the "top" overlays are currently visible. The check includes
1381
  * the call/ring overlay, the suspended overlay, the GUM permissions overlay,
1372
  * the call/ring overlay, the suspended overlay, the GUM permissions overlay,

+ 0
- 5
modules/devices/mediaDeviceHelper.js View File

55
             availableAudioInputDevices[0].label !== '') {
55
             availableAudioInputDevices[0].label !== '') {
56
             return availableAudioInputDevices[0].deviceId;
56
             return availableAudioInputDevices[0].deviceId;
57
         }
57
         }
58
-        // Otherwise we assume that we don't have any audio input devices
59
-        // to use and that's why disable microphone button on UI.
60
-        else {
61
-            APP.UI.setMicrophoneButtonEnabled(false);
62
-        }
63
     } else {
58
     } else {
64
         // And here we handle case when we already have some device working,
59
         // And here we handle case when we already have some device working,
65
         // but we plug-in a "preferred" (previously selected in settings, stored
60
         // but we plug-in a "preferred" (previously selected in settings, stored

+ 10
- 0
react/features/base/media/actionTypes.js View File

8
  */
8
  */
9
 export const SET_AUDIO_MUTED = Symbol('SET_AUDIO_MUTED');
9
 export const SET_AUDIO_MUTED = Symbol('SET_AUDIO_MUTED');
10
 
10
 
11
+/**
12
+ * The type of (redux) action to adjust the availability of the local audio.
13
+ *
14
+ * {
15
+ *     type: SET_AUDIO_AVAILABLE,
16
+ *     muted: boolean
17
+ * }
18
+ */
19
+export const SET_AUDIO_AVAILABLE = Symbol('SET_AUDIO_AVAILABLE');
20
+
11
 /**
21
 /**
12
  * The type of (redux) action to set the facing mode of the local video camera
22
  * The type of (redux) action to set the facing mode of the local video camera
13
  * to a specific value.
23
  * to a specific value.

+ 18
- 0
react/features/base/media/actions.js View File

4
 
4
 
5
 import {
5
 import {
6
     SET_AUDIO_MUTED,
6
     SET_AUDIO_MUTED,
7
+    SET_AUDIO_AVAILABLE,
7
     SET_CAMERA_FACING_MODE,
8
     SET_CAMERA_FACING_MODE,
8
     SET_VIDEO_AVAILABLE,
9
     SET_VIDEO_AVAILABLE,
9
     SET_VIDEO_MUTED,
10
     SET_VIDEO_MUTED,
11
 } from './actionTypes';
12
 } from './actionTypes';
12
 import { CAMERA_FACING_MODE } from './constants';
13
 import { CAMERA_FACING_MODE } from './constants';
13
 
14
 
15
+/**
16
+ * Action to adjust the availability of the local audio.
17
+ *
18
+ * @param {boolean} available - True if the local audio is to be marked as
19
+ * available or false if the local audio is not available.
20
+ * @returns {{
21
+ *      type: SET_AUDIO_AVAILABLE,
22
+ *      available: boolean
23
+ *  }}
24
+ */
25
+export function setAudioAvailable(available: boolean) {
26
+    return {
27
+        type: SET_AUDIO_AVAILABLE,
28
+        available
29
+    };
30
+}
31
+
14
 /**
32
 /**
15
  * Action to set the muted state of the local audio.
33
  * Action to set the muted state of the local audio.
16
  *
34
  *

+ 8
- 0
react/features/base/media/reducer.js View File

3
 import { ReducerRegistry } from '../redux';
3
 import { ReducerRegistry } from '../redux';
4
 
4
 
5
 import {
5
 import {
6
+    SET_AUDIO_AVAILABLE,
6
     SET_AUDIO_MUTED,
7
     SET_AUDIO_MUTED,
7
     SET_CAMERA_FACING_MODE,
8
     SET_CAMERA_FACING_MODE,
8
     SET_VIDEO_AVAILABLE,
9
     SET_VIDEO_AVAILABLE,
24
  * @type {AudioMediaState}
25
  * @type {AudioMediaState}
25
  */
26
  */
26
 const AUDIO_INITIAL_MEDIA_STATE = {
27
 const AUDIO_INITIAL_MEDIA_STATE = {
28
+    available: true,
27
     muted: false
29
     muted: false
28
 };
30
 };
29
 
31
 
38
  */
40
  */
39
 function _audio(state = AUDIO_INITIAL_MEDIA_STATE, action) {
41
 function _audio(state = AUDIO_INITIAL_MEDIA_STATE, action) {
40
     switch (action.type) {
42
     switch (action.type) {
43
+    case SET_AUDIO_AVAILABLE:
44
+        return {
45
+            ...state,
46
+            available: action.available
47
+        };
48
+
41
     case SET_AUDIO_MUTED:
49
     case SET_AUDIO_MUTED:
42
         return {
50
         return {
43
             ...state,
51
             ...state,

+ 0
- 21
react/features/toolbox/actions.native.js View File

51
     };
51
     };
52
 }
52
 }
53
 
53
 
54
-/**
55
- * Enables/disables audio toolbar button.
56
- *
57
- * @param {boolean} enabled - True if the button should be enabled; otherwise,
58
- * false.
59
- * @returns {Function}
60
- */
61
-export function setAudioIconEnabled(enabled: boolean = false): Function {
62
-    return (dispatch: Dispatch<*>) => {
63
-        const i18nKey = enabled ? 'mute' : 'micDisabled';
64
-        const i18n = `[content]toolbar.${i18nKey}`;
65
-        const button = {
66
-            enabled,
67
-            i18n,
68
-            toggled: !enabled
69
-        };
70
-
71
-        dispatch(setToolbarButton('microphone', button));
72
-    };
73
-}
74
-
75
 /**
54
 /**
76
  * Signals that value of conference subject should be changed.
55
  * Signals that value of conference subject should be changed.
77
  *
56
  *

+ 35
- 1
react/features/toolbox/middleware.js View File

1
 /* @flow */
1
 /* @flow */
2
 
2
 
3
-import { SET_VIDEO_AVAILABLE, SET_VIDEO_MUTED } from '../base/media';
3
+import {
4
+    SET_AUDIO_AVAILABLE,
5
+    SET_AUDIO_MUTED,
6
+    SET_VIDEO_AVAILABLE,
7
+    SET_VIDEO_MUTED } from '../base/media';
4
 import { MiddlewareRegistry } from '../base/redux';
8
 import { MiddlewareRegistry } from '../base/redux';
5
 
9
 
6
 import { setToolbarButton } from './actions';
10
 import { setToolbarButton } from './actions';
33
         break;
37
         break;
34
     }
38
     }
35
 
39
 
40
+    case SET_AUDIO_AVAILABLE:
41
+    case SET_AUDIO_MUTED: {
42
+        return _setAudioAvailableOrMuted(store, next, action);
43
+    }
44
+
36
     case SET_VIDEO_AVAILABLE:
45
     case SET_VIDEO_AVAILABLE:
37
     case SET_VIDEO_MUTED:
46
     case SET_VIDEO_MUTED:
38
         return _setVideoAvailableOrMuted(store, next, action);
47
         return _setVideoAvailableOrMuted(store, next, action);
41
     return next(action);
50
     return next(action);
42
 });
51
 });
43
 
52
 
53
+/**
54
+ * Adjusts the state of toolbar's microphone button.
55
+ *
56
+ * @param {Store} store - The Redux store instance.
57
+ * @param {Function} next - The redux function to continue dispatching the
58
+ * specified {@code action} in the specified {@code store}.
59
+ * @param {Object} action - Either SET_AUDIO_AVAILABLE or SET_AUDIO_MUTED.
60
+ *
61
+ * @returns {*}
62
+ */
63
+function _setAudioAvailableOrMuted({ dispatch, getState }, next, action) {
64
+    const result = next(action);
65
+
66
+    const { available, muted } = getState()['features/base/media'].audio;
67
+    const i18nKey = available ? 'mute' : 'micDisabled';
68
+
69
+    dispatch(setToolbarButton('microphone', {
70
+        enabled: available,
71
+        i18n: `[content]toolbar.${i18nKey}`,
72
+        toggled: available ? muted : true
73
+    }));
74
+
75
+    return result;
76
+}
77
+
44
 /**
78
 /**
45
  * Adjusts the state of toolbar's camera button.
79
  * Adjusts the state of toolbar's camera button.
46
  *
80
  *

Loading…
Cancel
Save