From 78fbfba5736416c7830246ae5ace9c68c513c962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Wed, 24 Jan 2018 15:41:48 +0100 Subject: [PATCH] [iOS] Fix initial CallKit muted state Turns out this was a bit more involved than I originally thought due to an interesting (corner) case: IFF the user was never asked about microphone permissions and the call starts with audio muted, unmuting from the CallKit interface won't work (iOS won't show the prompt, it fails immediately) and we need to sync the mute state back. --- react/features/mobile/callkit/middleware.js | 48 ++++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/react/features/mobile/callkit/middleware.js b/react/features/mobile/callkit/middleware.js index d1f51c91a..2c39a997d 100644 --- a/react/features/mobile/callkit/middleware.js +++ b/react/features/mobile/callkit/middleware.js @@ -17,12 +17,18 @@ import { } from '../../base/conference'; import { getInviteURL } from '../../base/connection'; import { - isVideoMutedByAudioOnly, + MEDIA_TYPE, SET_AUDIO_MUTED, SET_VIDEO_MUTED, + VIDEO_MUTISM_AUTHORITY, + isVideoMutedByAudioOnly, setAudioMuted } from '../../base/media'; import { MiddlewareRegistry } from '../../base/redux'; +import { + TRACK_CREATE_ERROR, + isLocalTrackMuted +} from '../../base/tracks'; import { _SET_CALLKIT_SUBSCRIPTIONS } from './actionTypes'; import CallKit from './CallKit'; @@ -65,6 +71,9 @@ CallKit && MiddlewareRegistry.register(store => next => action => { case SET_VIDEO_MUTED: return _setVideoMuted(store, next, action); + + case TRACK_CREATE_ERROR: + return _trackCreateError(store, next, action); } return next(action); @@ -230,10 +239,14 @@ function _conferenceWillJoin({ getState }, next, action) { .then(() => { const { room } = state['features/base/conference']; const { callee } = state['features/base/jwt']; + const tracks = state['features/base/tracks']; + const muted = isLocalTrackMuted(tracks, MEDIA_TYPE.AUDIO); CallKit.updateCall( conference.callUUID, { displayName: (callee && callee.name) || room }); + + CallKit.setMuted(conference.callUUID, muted); }); return result; @@ -280,7 +293,8 @@ function _onPerformSetMutedCallAction({ callUUID, muted: newValue }) { const value = Boolean(newValue); sendAnalytics(createTrackMutedEvent('audio', 'callkit', value)); - dispatch(setAudioMuted(value)); + dispatch(setAudioMuted( + value, VIDEO_MUTISM_AUTHORITY.USER, /* ensureTrack */ true)); } } } @@ -361,3 +375,33 @@ function _setVideoMuted({ getState }, next, action) { return result; } + +/** + * Handles a track creation failure. This is relevant to us in the following + * (corner) case: if the user never gave their permission to use the microphone + * and try to unmute from the CallKit interface, this will fail, and we need + * to sync back the CallKit button state. + * + * @param {Store} store - The redux store in which the specified {@code action} + * is being dispatched. + * @param {Dispatch} next - The redux dispatch function to dispatch the + * specified {@code action} to the specified {@code store}. + * @param {Action} action - The redux action {@code TRACK_CREARE_ERROR} which is + * being dispatched in the specified {@code store}. + * @private + * @returns {*} + */ +function _trackCreateError({ getState }, next, action) { + const result = next(action); + const state = getState(); + const conference = getCurrentConference(state); + + if (conference && conference.callUUID) { + const tracks = state['features/base/tracks']; + const muted = isLocalTrackMuted(tracks, MEDIA_TYPE.AUDIO); + + CallKit.setMuted(conference.callUUID, muted); + } + + return result; +}