diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsState.kt index 9132a22542..046ee44f4d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsState.kt @@ -197,6 +197,7 @@ data class CallParticipantsState( companion object { const val SMALL_GROUP_MAX = 6 + const val PRE_JOIN_MUTE_THRESHOLD = 8 @JvmField val MAX_OUTGOING_GROUP_RING_DURATION = TimeUnit.MINUTES.toMillis(1) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallEvent.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallEvent.kt index 7396775ddb..c60a04883e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallEvent.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/CallEvent.kt @@ -23,6 +23,7 @@ sealed interface CallEvent { data class ShowGroupCallSafetyNumberChange(val identityRecords: List) : CallEvent data object SwitchToSpeaker : CallEvent data object ShowSwipeToSpeakerHint : CallEvent + data object ShowLargeGroupAutoMuteToast : CallEvent data class ShowRemoteMuteToast(private val muted: Recipient, private val mutedBy: Recipient) : CallEvent { fun getDescription(context: Context): String { return if (muted.isSelf && mutedBy.isSelf) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/WebRtcCallActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/WebRtcCallActivity.kt index 1453918c97..518df0bc3c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/WebRtcCallActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/WebRtcCallActivity.kt @@ -960,6 +960,10 @@ class WebRtcCallActivity : BaseActivity(), SafetyNumberChangeDialog.Callback, Re is CallEvent.SwitchToSpeaker -> callScreen.switchToSpeakerView() is CallEvent.ShowSwipeToSpeakerHint -> callScreen.showSpeakerViewHint() is CallEvent.ShowRemoteMuteToast -> callScreen.showRemoteMuteToast(event.getDescription(this)) + is CallEvent.ShowLargeGroupAutoMuteToast -> { + callScreen.onCallStateUpdate(CallControlsChange.MIC_OFF) + callScreen.showRemoteMuteToast(getString(R.string.WebRtcCallView__youve_been_muted_large_group)) + } is CallEvent.ShowVideoTooltip -> { if (isInPipMode()) return diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/WebRtcCallViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/WebRtcCallViewModel.kt index 93aa11b40c..b1346f06d9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/WebRtcCallViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/v2/WebRtcCallViewModel.kt @@ -101,6 +101,7 @@ class WebRtcCallViewModel : ViewModel() { private var previousParticipantList = Collections.emptyList() private var switchOnFirstScreenShare = true private var showScreenShareTip = true + private var hasShownAutoMuteToast = false var isCallStarting = false private set @@ -313,6 +314,7 @@ class WebRtcCallViewModel : ViewModel() { } } + val wasMicrophoneEnabled = internalMicrophoneEnabled.value internalMicrophoneEnabled.value = localParticipant.isMicrophoneEnabled isAudioDeviceChangePending.value = webRtcViewModel.isAudioDeviceChangePending @@ -320,6 +322,16 @@ class WebRtcCallViewModel : ViewModel() { remoteMutedBy.update { null } } + if (!hasShownAutoMuteToast && + wasMicrophoneEnabled && + !localParticipant.isMicrophoneEnabled && + webRtcViewModel.state == WebRtcViewModel.State.CALL_PRE_JOIN && + webRtcViewModel.remoteDevicesCount.orElse(0L) >= CallParticipantsState.PRE_JOIN_MUTE_THRESHOLD + ) { + hasShownAutoMuteToast = true + emitEvent(CallEvent.ShowLargeGroupAutoMuteToast) + } + val state: CallParticipantsState = participantsState.value!! val wasScreenSharing: Boolean = state.focusedParticipant.isScreenSharing val newState: CallParticipantsState = CallParticipantsState.update(state, webRtcViewModel, enableVideo) diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java index 1e6a9cce3e..ba8e4e94e3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java @@ -9,6 +9,7 @@ import org.signal.ringrtc.CallException; import org.signal.ringrtc.GroupCall; import org.signal.ringrtc.PeekInfo; import org.thoughtcrime.securesms.components.webrtc.BroadcastVideoSink; +import org.thoughtcrime.securesms.components.webrtc.CallParticipantsState; import org.thoughtcrime.securesms.components.webrtc.EglBaseWrapper; import org.thoughtcrime.securesms.events.CallParticipant; import org.thoughtcrime.securesms.events.CallParticipantId; @@ -149,7 +150,16 @@ public class GroupPreJoinActionProcessor extends GroupActionProcessor { CallParticipant.DeviceOrdinal.PRIMARY)); } - return builder.build(); + WebRtcServiceStateBuilder stateBuilder = builder.commit(); + + if (peekInfo.getDeviceCountExcludingPendingDevices() >= CallParticipantsState.PRE_JOIN_MUTE_THRESHOLD && currentState.getLocalDeviceState().isMicrophoneEnabled()) { + Log.i(tag, "Large call detected (" + peekInfo.getDeviceCountExcludingPendingDevices() + " participants), auto-muting microphone"); + return stateBuilder.changeLocalDeviceState() + .isMicrophoneEnabled(false) + .build(); + } + + return stateBuilder.build(); } @Override diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 287886dfd8..65814f3a59 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2581,6 +2581,8 @@ %1$s muted %2$s. You muted yourself from another device. + + You\'ve been muted because this call has many participants. A UI error occurred. Please report this error to the developers.