Receive calling reactions support and control ux refactor.

Co-authored-by: Nicholas <nicholas@signal.org>
This commit is contained in:
Cody Henthorne
2023-12-06 13:35:19 -05:00
parent 7ce2991b0f
commit a678555d8d
36 changed files with 1852 additions and 747 deletions

View File

@@ -95,7 +95,8 @@ public class ConnectedCallActionProcessor extends DeviceAwareActionProcessor {
return ephemeralState.copy(
CallParticipant.AudioLevel.fromRawAudioLevel(localLevel),
callParticipantId.map(participantId -> Collections.singletonMap(participantId, CallParticipant.AudioLevel.fromRawAudioLevel(remoteLevel)))
.orElse(Collections.emptyMap())
.orElse(Collections.emptyMap()),
ephemeralState.getUnexpiredReactions()
);
}

View File

@@ -14,6 +14,7 @@ import org.signal.ringrtc.GroupCall;
import org.signal.ringrtc.PeekInfo;
import org.thoughtcrime.securesms.events.CallParticipant;
import org.thoughtcrime.securesms.events.CallParticipantId;
import org.thoughtcrime.securesms.events.GroupCallReactionEvent;
import org.thoughtcrime.securesms.events.WebRtcViewModel;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -25,7 +26,10 @@ import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* Process actions for when the call has at least once been connected and joined.
@@ -135,7 +139,7 @@ public class GroupConnectedActionProcessor extends GroupActionProcessor {
}
}
return ephemeralState.copy(localAudioLevel, remoteAudioLevels);
return ephemeralState.copy(localAudioLevel, remoteAudioLevels, ephemeralState.getUnexpiredReactions());
}
@Override
@@ -197,4 +201,30 @@ public class GroupConnectedActionProcessor extends GroupActionProcessor {
return terminateGroupCall(currentState);
}
@Override
protected @NonNull WebRtcEphemeralState handleGroupCallReaction(@NonNull WebRtcServiceState currentState, @NonNull WebRtcEphemeralState ephemeralState, List<GroupCall.Reaction> reactions) {
List<GroupCallReactionEvent> reactionList = ephemeralState.getUnexpiredReactions();
Map<CallParticipantId, CallParticipant> participants = currentState.getCallInfoState().getRemoteCallParticipantsMap();
for (GroupCall.Reaction reaction : reactions) {
final GroupCallReactionEvent event = createGroupCallReaction(participants, reaction);
if (event != null) {
reactionList.add(event);
}
}
return ephemeralState.copy(ephemeralState.getLocalAudioLevel(), ephemeralState.getRemoteAudioLevels(), reactionList);
}
@Nullable
private GroupCallReactionEvent createGroupCallReaction(Map<CallParticipantId, CallParticipant> participants, final GroupCall.Reaction reaction) {
CallParticipantId participantId = participants.keySet().stream().filter(participant -> participant.getDemuxId() == reaction.demuxId).findFirst().orElse(null);
if (participantId == null) {
Log.v(TAG, "Could not find CallParticipantId in list of call participants based on demuxId for reaction.");
return null;
}
return new GroupCallReactionEvent(participants.get(participantId), reaction.value, System.currentTimeMillis());
}
}

View File

@@ -295,6 +295,10 @@ public final class SignalCallManager implements CallManager.Observer, GroupCall.
process((s, p) -> p.handleScreenOffChange(s));
}
public void react() {
process((s, p) -> p.handleSendGroupReact(s));
}
public void postStateUpdate(@NonNull WebRtcServiceState state) {
EventBus.getDefault().postSticky(new WebRtcViewModel(state));
}
@@ -898,7 +902,9 @@ public final class SignalCallManager implements CallManager.Observer, GroupCall.
@Override
public void onReactions(@NonNull GroupCall groupCall, List<Reaction> reactions) {
// TODO: Implement handling of reactions.
if (FeatureFlags.groupCallReactions()) {
processStateless(s -> serviceState.getActionProcessor().handleGroupCallReaction(serviceState, s, reactions));
}
}
@Override

View File

@@ -546,6 +546,11 @@ public abstract class WebRtcActionProcessor {
return currentState;
}
protected @NonNull WebRtcServiceState handleSendGroupReact(@NonNull WebRtcServiceState currentState) {
Log.i(tag, "react not processed");
return currentState;
}
public @NonNull WebRtcServiceState handleCameraSwitchCompleted(@NonNull WebRtcServiceState currentState, @NonNull CameraState newCameraState) {
Log.i(tag, "handleCameraSwitchCompleted not processed");
return currentState;
@@ -729,6 +734,11 @@ public abstract class WebRtcActionProcessor {
return currentState;
}
protected @NonNull WebRtcEphemeralState handleGroupCallReaction(@NonNull WebRtcServiceState currentState, @NonNull WebRtcEphemeralState ephemeralState, List<GroupCall.Reaction> reactions) {
Log.i(tag, "handleGroupCallReaction not processed");
return ephemeralState;
}
protected @NonNull WebRtcServiceState handleGroupRequestMembershipProof(@NonNull WebRtcServiceState currentState, int groupCallHashCode) {
Log.i(tag, "handleGroupRequestMembershipProof not processed");
return currentState;

View File

@@ -2,11 +2,18 @@ package org.thoughtcrime.securesms.service.webrtc.state
import org.thoughtcrime.securesms.events.CallParticipant
import org.thoughtcrime.securesms.events.CallParticipantId
import org.thoughtcrime.securesms.events.GroupCallReactionEvent
/**
* The state of the call system which contains data which changes frequently.
*/
data class WebRtcEphemeralState(
val localAudioLevel: CallParticipant.AudioLevel = CallParticipant.AudioLevel.LOWEST,
val remoteAudioLevels: Map<CallParticipantId, CallParticipant.AudioLevel> = emptyMap()
)
val remoteAudioLevels: Map<CallParticipantId, CallParticipant.AudioLevel> = emptyMap(),
private val reactions: List<GroupCallReactionEvent> = emptyList()
) {
fun getUnexpiredReactions(): List<GroupCallReactionEvent> {
return reactions.filter { System.currentTimeMillis() < it.getExpirationTimestamp() }
}
}