Signal-Android/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor...

219 wiersze
10 KiB
Java

package org.thoughtcrime.securesms.service.webrtc;
import androidx.annotation.NonNull;
import com.annimon.stream.Stream;
import org.signal.core.util.logging.Log;
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.EglBaseWrapper;
import org.thoughtcrime.securesms.events.CallParticipant;
import org.thoughtcrime.securesms.events.CallParticipantId;
import org.thoughtcrime.securesms.events.WebRtcViewModel;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.ringrtc.RemotePeer;
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState;
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceStateBuilder;
import org.thoughtcrime.securesms.util.NetworkUtil;
import org.whispersystems.signalservice.api.messages.calls.OfferMessage;
import org.whispersystems.signalservice.api.push.ServiceId;
import java.util.List;
import static org.thoughtcrime.securesms.webrtc.CallNotificationBuilder.TYPE_OUTGOING_RINGING;
/**
* Process actions while the user is in the pre-join lobby for the call.
*/
public class GroupPreJoinActionProcessor extends GroupActionProcessor {
private static final String TAG = Log.tag(GroupPreJoinActionProcessor.class);
public GroupPreJoinActionProcessor(@NonNull WebRtcInteractor webRtcInteractor) {
super(webRtcInteractor, TAG);
}
@Override
protected @NonNull WebRtcServiceState handlePreJoinCall(@NonNull WebRtcServiceState currentState, @NonNull RemotePeer remotePeer) {
Log.i(TAG, "handlePreJoinCall():");
byte[] groupId = currentState.getCallInfoState().getCallRecipient().requireGroupId().getDecodedId();
GroupCall groupCall = webRtcInteractor.getCallManager().createGroupCall(groupId,
SignalStore.internalValues().groupCallingServer(),
new byte[0],
AUDIO_LEVELS_INTERVAL,
RingRtcDynamicConfiguration.getAudioProcessingMethod(),
webRtcInteractor.getGroupCallObserver());
try {
groupCall.setOutgoingAudioMuted(true);
groupCall.setOutgoingVideoMuted(true);
groupCall.setBandwidthMode(NetworkUtil.getCallingBandwidthMode(context, groupCall.getLocalDeviceState().getNetworkRoute().getLocalAdapterType()));
Log.i(TAG, "Connecting to group call: " + currentState.getCallInfoState().getCallRecipient().getId());
groupCall.connect();
} catch (CallException e) {
return groupCallFailure(currentState, "Unable to connect to group call", e);
}
SignalStore.tooltips().markGroupCallingLobbyEntered();
return currentState.builder()
.changeCallInfoState()
.groupCall(groupCall)
.groupCallState(WebRtcViewModel.GroupCallState.DISCONNECTED)
.activePeer(new RemotePeer(currentState.getCallInfoState().getCallRecipient().getId(), RemotePeer.GROUP_CALL_ID))
.build();
}
@Override
protected @NonNull WebRtcServiceState handleCancelPreJoinCall(@NonNull WebRtcServiceState currentState) {
Log.i(TAG, "handleCancelPreJoinCall():");
GroupCall groupCall = currentState.getCallInfoState().requireGroupCall();
try {
groupCall.disconnect();
} catch (CallException e) {
return groupCallFailure(currentState, "Unable to disconnect from group call", e);
}
WebRtcVideoUtil.deinitializeVideo(currentState);
EglBaseWrapper.releaseEglBase(RemotePeer.GROUP_CALL_ID.longValue());
return new WebRtcServiceState(new IdleActionProcessor(webRtcInteractor));
}
@Override
protected @NonNull WebRtcServiceState handleGroupLocalDeviceStateChanged(@NonNull WebRtcServiceState currentState) {
Log.i(tag, "handleGroupLocalDeviceStateChanged():");
currentState = super.handleGroupLocalDeviceStateChanged(currentState);
GroupCall groupCall = currentState.getCallInfoState().requireGroupCall();
GroupCall.LocalDeviceState device = groupCall.getLocalDeviceState();
Log.i(tag, "local device changed: " + device.getConnectionState() + " " + device.getJoinState());
return currentState.builder()
.changeCallInfoState()
.groupCallState(WebRtcUtil.groupCallStateForConnection(device.getConnectionState()))
.build();
}
@Override
protected @NonNull WebRtcServiceState handleGroupJoinedMembershipChanged(@NonNull WebRtcServiceState currentState) {
Log.i(tag, "handleGroupJoinedMembershipChanged():");
GroupCall groupCall = currentState.getCallInfoState().requireGroupCall();
PeekInfo peekInfo = groupCall.getPeekInfo();
if (peekInfo == null) {
Log.i(tag, "No peek info available");
return currentState;
}
List<Recipient> callParticipants = Stream.of(peekInfo.getJoinedMembers())
.map(uuid -> Recipient.externalPush(ServiceId.from(uuid)))
.toList();
WebRtcServiceStateBuilder.CallInfoStateBuilder builder = currentState.builder()
.changeCallInfoState()
.remoteDevicesCount(peekInfo.getDeviceCount())
.participantLimit(peekInfo.getMaxDevices())
.clearParticipantMap();
for (Recipient recipient : callParticipants) {
builder.putParticipant(recipient, CallParticipant.createRemote(new CallParticipantId(recipient),
recipient,
null,
new BroadcastVideoSink(),
true,
true,
true,
0,
false,
0,
false,
CallParticipant.DeviceOrdinal.PRIMARY));
}
return builder.build();
}
@Override
protected @NonNull WebRtcServiceState handleOutgoingCall(@NonNull WebRtcServiceState currentState,
@NonNull RemotePeer remotePeer,
@NonNull OfferMessage.Type offerType)
{
Log.i(TAG, "handleOutgoingCall():");
GroupCall groupCall = currentState.getCallInfoState().requireGroupCall();
currentState = WebRtcVideoUtil.reinitializeCamera(context, webRtcInteractor.getCameraEventListener(), currentState);
webRtcInteractor.setCallInProgressNotification(TYPE_OUTGOING_RINGING, currentState.getCallInfoState().getCallRecipient());
webRtcInteractor.updatePhoneState(WebRtcUtil.getInCallPhoneState(context));
webRtcInteractor.initializeAudioForCall();
try {
groupCall.setOutgoingVideoSource(currentState.getVideoState().requireLocalSink(), currentState.getVideoState().requireCamera());
groupCall.setOutgoingVideoMuted(!currentState.getLocalDeviceState().getCameraState().isEnabled());
groupCall.setOutgoingAudioMuted(!currentState.getLocalDeviceState().isMicrophoneEnabled());
groupCall.setBandwidthMode(NetworkUtil.getCallingBandwidthMode(context, groupCall.getLocalDeviceState().getNetworkRoute().getLocalAdapterType()));
groupCall.join();
} catch (CallException e) {
return groupCallFailure(currentState, "Unable to join group call", e);
}
return currentState.builder()
.actionProcessor(new GroupJoiningActionProcessor(webRtcInteractor))
.changeCallInfoState()
.callState(WebRtcViewModel.State.CALL_OUTGOING)
.groupCallState(WebRtcViewModel.GroupCallState.CONNECTED_AND_JOINING)
.commit()
.changeLocalDeviceState()
.build();
}
@Override
protected @NonNull WebRtcServiceState handleSetEnableVideo(@NonNull WebRtcServiceState currentState, boolean enable) {
Log.i(TAG, "handleSetEnableVideo(): Changing for pre-join group call. enable: " + enable);
currentState.getVideoState().requireCamera().setEnabled(enable);
return currentState.builder()
.changeCallSetupState(RemotePeer.GROUP_CALL_ID)
.enableVideoOnCreate(enable)
.commit()
.changeLocalDeviceState()
.cameraState(currentState.getVideoState().requireCamera().getCameraState())
.build();
}
@Override
protected @NonNull WebRtcServiceState handleSetMuteAudio(@NonNull WebRtcServiceState currentState, boolean muted) {
Log.i(TAG, "handleSetMuteAudio(): Changing for pre-join group call. muted: " + muted);
return currentState.builder()
.changeLocalDeviceState()
.isMicrophoneEnabled(!muted)
.build();
}
@Override
public @NonNull WebRtcServiceState handleNetworkChanged(@NonNull WebRtcServiceState currentState, boolean available) {
if (!available) {
return currentState.builder()
.actionProcessor(new GroupNetworkUnavailableActionProcessor(webRtcInteractor))
.changeCallInfoState()
.callState(WebRtcViewModel.State.NETWORK_FAILURE)
.build();
} else {
return currentState;
}
}
}