Improve group update copy and implement speaker indexing.

This commit is contained in:
Cody Henthorne 2020-11-24 13:23:25 -05:00 committed by Alex Hart
parent ce68da1613
commit a640d9e298
15 changed files with 145 additions and 52 deletions

View file

@ -5,12 +5,12 @@ import android.content.Context;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.annimon.stream.ComparatorCompat;
import com.annimon.stream.Stream; import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.events.CallParticipant; import org.thoughtcrime.securesms.events.CallParticipant;
import org.thoughtcrime.securesms.events.WebRtcViewModel; import org.thoughtcrime.securesms.events.WebRtcViewModel;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.ringrtc.CameraState; import org.thoughtcrime.securesms.ringrtc.CameraState;
import java.util.ArrayList; import java.util.ArrayList;
@ -171,7 +171,10 @@ public final class CallParticipantsState {
webRtcViewModel.getRemoteParticipants().size(), webRtcViewModel.getRemoteParticipants().size(),
oldState.isViewingFocusedParticipant); oldState.isViewingFocusedParticipant);
CallParticipant focused = oldState.remoteParticipants.isEmpty() ? null : oldState.remoteParticipants.get(0); List<CallParticipant> participantsByLastSpoke = new ArrayList<>(webRtcViewModel.getRemoteParticipants());
Collections.sort(participantsByLastSpoke, ComparatorCompat.reversed((p1, p2) -> Long.compare(p1.getLastSpoke(), p2.getLastSpoke())));
CallParticipant focused = participantsByLastSpoke.isEmpty() ? null : participantsByLastSpoke.get(0);
return new CallParticipantsState(webRtcViewModel.getState(), return new CallParticipantsState(webRtcViewModel.getState(),
webRtcViewModel.getGroupState(), webRtcViewModel.getGroupState(),

View file

@ -183,7 +183,7 @@ public final class ConversationUpdateItem extends LinearLayout
}); });
} else if (conversationMessage.getMessageRecord().isGroupCall()) { } else if (conversationMessage.getMessageRecord().isGroupCall()) {
UpdateDescription updateDescription = MessageRecord.getGroupCallUpdateDescription(getContext(), conversationMessage.getMessageRecord().getBody()); UpdateDescription updateDescription = MessageRecord.getGroupCallUpdateDescription(getContext(), conversationMessage.getMessageRecord().getBody(), true);
Collection<UUID> uuids = updateDescription.getMentioned(); Collection<UUID> uuids = updateDescription.getMentioned();
int text = 0; int text = 0;

View file

@ -462,7 +462,7 @@ public final class ConversationListItem extends RelativeLayout
} else if (SmsDatabase.Types.isMissedVideoCall(thread.getType())) { } else if (SmsDatabase.Types.isMissedVideoCall(thread.getType())) {
return emphasisAdded(context, context.getString(R.string.ThreadRecord_missed_video_call), defaultTint); return emphasisAdded(context, context.getString(R.string.ThreadRecord_missed_video_call), defaultTint);
} else if (MmsSmsColumns.Types.isGroupCall(thread.getType())) { } else if (MmsSmsColumns.Types.isGroupCall(thread.getType())) {
return emphasisAdded(context, MessageRecord.getGroupCallUpdateDescription(context, thread.getBody()), defaultTint); return emphasisAdded(context, MessageRecord.getGroupCallUpdateDescription(context, thread.getBody(), false), defaultTint);
} else if (SmsDatabase.Types.isJoinedType(thread.getType())) { } else if (SmsDatabase.Types.isJoinedType(thread.getType())) {
return emphasisAdded(recipientToStringAsync(thread.getRecipient().getId(), r -> new SpannableString(context.getString(R.string.ThreadRecord_s_is_on_signal, r.getDisplayName(context))))); return emphasisAdded(recipientToStringAsync(thread.getRecipient().getId(), r -> new SpannableString(context.getString(R.string.ThreadRecord_s_is_on_signal, r.getDisplayName(context)))));
} else if (SmsDatabase.Types.isExpirationTimerUpdate(thread.getType())) { } else if (SmsDatabase.Types.isExpirationTimerUpdate(thread.getType())) {

View file

@ -704,6 +704,9 @@ public class SmsDatabase extends MessageDatabase {
boolean peerEraIdSameAsPrevious = updatePreviousGroupCall(threadId, peekGroupCallEraId, peekJoinedUuids); boolean peerEraIdSameAsPrevious = updatePreviousGroupCall(threadId, peekGroupCallEraId, peekJoinedUuids);
if (!peerEraIdSameAsPrevious && !Util.isEmpty(peekGroupCallEraId)) { if (!peerEraIdSameAsPrevious && !Util.isEmpty(peekGroupCallEraId)) {
Recipient self = Recipient.self();
boolean markRead = peekJoinedUuids.contains(self.requireUuid()) || self.getId().equals(sender);
byte[] updateDetails = GroupCallUpdateDetails.newBuilder() byte[] updateDetails = GroupCallUpdateDetails.newBuilder()
.setEraId(Util.emptyIfNull(peekGroupCallEraId)) .setEraId(Util.emptyIfNull(peekGroupCallEraId))
.setStartedCallUuid(Recipient.resolved(sender).requireUuid().toString()) .setStartedCallUuid(Recipient.resolved(sender).requireUuid().toString())
@ -719,7 +722,7 @@ public class SmsDatabase extends MessageDatabase {
values.put(ADDRESS_DEVICE_ID, 1); values.put(ADDRESS_DEVICE_ID, 1);
values.put(DATE_RECEIVED, timestamp); values.put(DATE_RECEIVED, timestamp);
values.put(DATE_SENT, timestamp); values.put(DATE_SENT, timestamp);
values.put(READ, 0); values.put(READ, markRead ? 1 : 0);
values.put(BODY, body); values.put(BODY, body);
values.put(TYPE, Types.GROUP_CALL_TYPE); values.put(TYPE, Types.GROUP_CALL_TYPE);
values.put(THREAD_ID, threadId); values.put(THREAD_ID, threadId);
@ -753,6 +756,7 @@ public class SmsDatabase extends MessageDatabase {
GroupCallUpdateDetails groupCallUpdateDetails = GroupCallUpdateDetailsUtil.parse(record.getBody()); GroupCallUpdateDetails groupCallUpdateDetails = GroupCallUpdateDetailsUtil.parse(record.getBody());
boolean sameEraId = groupCallUpdateDetails.getEraId().equals(peekGroupCallEraId) && !Util.isEmpty(peekGroupCallEraId); boolean sameEraId = groupCallUpdateDetails.getEraId().equals(peekGroupCallEraId) && !Util.isEmpty(peekGroupCallEraId);
boolean containsSelf = peekJoinedUuids.contains(Recipient.self().requireUuid());
List<String> inCallUuids = sameEraId ? Stream.of(peekJoinedUuids).map(UUID::toString).toList() List<String> inCallUuids = sameEraId ? Stream.of(peekJoinedUuids).map(UUID::toString).toList()
: Collections.emptyList(); : Collections.emptyList();
@ -762,6 +766,10 @@ public class SmsDatabase extends MessageDatabase {
ContentValues contentValues = new ContentValues(); ContentValues contentValues = new ContentValues();
contentValues.put(BODY, body); contentValues.put(BODY, body);
if (sameEraId && containsSelf) {
contentValues.put(READ, 1);
}
db.update(TABLE_NAME, contentValues, ID_WHERE, SqlUtil.buildArgs(record.getId())); db.update(TABLE_NAME, contentValues, ID_WHERE, SqlUtil.buildArgs(record.getId()));
return sameEraId; return sameEraId;

View file

@ -25,12 +25,18 @@ import java.util.UUID;
public class GroupCallUpdateMessageFactory implements UpdateDescription.StringFactory { public class GroupCallUpdateMessageFactory implements UpdateDescription.StringFactory {
private final Context context; private final Context context;
private final List<UUID> joinedMembers; private final List<UUID> joinedMembers;
private final boolean withTime;
private final GroupCallUpdateDetails groupCallUpdateDetails; private final GroupCallUpdateDetails groupCallUpdateDetails;
private final UUID selfUuid; private final UUID selfUuid;
public GroupCallUpdateMessageFactory(@NonNull Context context, @NonNull List<UUID> joinedMembers, @NonNull GroupCallUpdateDetails groupCallUpdateDetails) { public GroupCallUpdateMessageFactory(@NonNull Context context,
@NonNull List<UUID> joinedMembers,
boolean withTime,
@NonNull GroupCallUpdateDetails groupCallUpdateDetails)
{
this.context = context; this.context = context;
this.joinedMembers = new ArrayList<>(joinedMembers); this.joinedMembers = new ArrayList<>(joinedMembers);
this.withTime = withTime;
this.groupCallUpdateDetails = groupCallUpdateDetails; this.groupCallUpdateDetails = groupCallUpdateDetails;
this.selfUuid = TextSecurePreferences.getLocalUuid(context); this.selfUuid = TextSecurePreferences.getLocalUuid(context);
@ -46,28 +52,40 @@ public class GroupCallUpdateMessageFactory implements UpdateDescription.StringFa
switch (joinedMembers.size()) { switch (joinedMembers.size()) {
case 0: case 0:
return context.getString(R.string.MessageRecord_group_call_s, time); return withTime ? context.getString(R.string.MessageRecord_group_call_s, time)
: context.getString(R.string.MessageRecord_group_call);
case 1: case 1:
if (joinedMembers.get(0).toString().equals(groupCallUpdateDetails.getStartedCallUuid())) { if (joinedMembers.get(0).toString().equals(groupCallUpdateDetails.getStartedCallUuid())) {
return context.getString(R.string.MessageRecord_s_started_a_group_call_s, describe(joinedMembers.get(0)), time); return withTime ? context.getString(R.string.MessageRecord_s_started_a_group_call_s, describe(joinedMembers.get(0)), time)
: context.getString(R.string.MessageRecord_s_started_a_group_call, describe(joinedMembers.get(0)));
} else if (Objects.equals(joinedMembers.get(0), selfUuid)) { } else if (Objects.equals(joinedMembers.get(0), selfUuid)) {
return context.getString(R.string.MessageRecord_you_are_in_the_group_call_s, describe(joinedMembers.get(0)), time); return withTime ? context.getString(R.string.MessageRecord_you_are_in_the_group_call_s1, time)
: context.getString(R.string.MessageRecord_you_are_in_the_group_call);
} else { } else {
return context.getString(R.string.MessageRecord_s_is_in_the_group_call_s, describe(joinedMembers.get(0)), time); return withTime ? context.getString(R.string.MessageRecord_s_is_in_the_group_call_s, describe(joinedMembers.get(0)), time)
: context.getString(R.string.MessageRecord_s_is_in_the_group_call, describe(joinedMembers.get(0)));
} }
case 2: case 2:
return context.getString(R.string.MessageRecord_s_and_s_are_in_the_group_call_s, return withTime ? context.getString(R.string.MessageRecord_s_and_s_are_in_the_group_call_s1,
describe(joinedMembers.get(0)), describe(joinedMembers.get(0)),
describe(joinedMembers.get(1)), describe(joinedMembers.get(1)),
time); time)
: context.getString(R.string.MessageRecord_s_and_s_are_in_the_group_call,
describe(joinedMembers.get(0)),
describe(joinedMembers.get(1)));
default: default:
int others = joinedMembers.size() - 2; int others = joinedMembers.size() - 2;
return context.getResources().getQuantityString(R.plurals.MessageRecord_s_s_and_d_others_are_in_the_group_call_s, return withTime ? context.getResources().getQuantityString(R.plurals.MessageRecord_s_s_and_d_others_are_in_the_group_call_s,
others, others,
describe(joinedMembers.get(0)), describe(joinedMembers.get(0)),
describe(joinedMembers.get(1)), describe(joinedMembers.get(1)),
others, others,
time); time)
: context.getResources().getQuantityString(R.plurals.MessageRecord_s_s_and_d_others_are_in_the_group_call,
others,
describe(joinedMembers.get(0)),
describe(joinedMembers.get(1)),
others);
} }
} }

View file

@ -162,7 +162,7 @@ public abstract class MessageRecord extends DisplayRecord {
} else if (isMissedVideoCall()) { } else if (isMissedVideoCall()) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_missed_video_call_date, getCallDateString(context)), R.drawable.ic_update_video_call_missed_16, ContextCompat.getColor(context, R.color.core_red_shade), ContextCompat.getColor(context, R.color.core_red)); return staticUpdateDescription(context.getString(R.string.MessageRecord_missed_video_call_date, getCallDateString(context)), R.drawable.ic_update_video_call_missed_16, ContextCompat.getColor(context, R.color.core_red_shade), ContextCompat.getColor(context, R.color.core_red));
} else if (isGroupCall()) { } else if (isGroupCall()) {
return getGroupCallUpdateDescription(context, getBody()); return getGroupCallUpdateDescription(context, getBody(), true);
} else if (isJoined()) { } else if (isJoined()) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_s_joined_signal, getIndividualRecipient().getDisplayName(context)), R.drawable.ic_update_group_add_16); return staticUpdateDescription(context.getString(R.string.MessageRecord_s_joined_signal, getIndividualRecipient().getDisplayName(context)), R.drawable.ic_update_group_add_16);
} else if (isExpirationTimerUpdate()) { } else if (isExpirationTimerUpdate()) {
@ -308,8 +308,7 @@ public abstract class MessageRecord extends DisplayRecord {
} }
} }
public static @NonNull UpdateDescription getGroupCallUpdateDescription(@NonNull Context context, @NonNull String body, boolean withTime) {
public static @NonNull UpdateDescription getGroupCallUpdateDescription(@NonNull Context context, @NonNull String body) {
GroupCallUpdateDetails groupCallUpdateDetails = GroupCallUpdateDetailsUtil.parse(body); GroupCallUpdateDetails groupCallUpdateDetails = GroupCallUpdateDetailsUtil.parse(body);
List<UUID> joinedMembers = Stream.of(groupCallUpdateDetails.getInCallUuidsList()) List<UUID> joinedMembers = Stream.of(groupCallUpdateDetails.getInCallUuidsList())
@ -317,7 +316,7 @@ public abstract class MessageRecord extends DisplayRecord {
.withoutNulls() .withoutNulls()
.toList(); .toList();
UpdateDescription.StringFactory stringFactory = new GroupCallUpdateMessageFactory(context, joinedMembers, groupCallUpdateDetails); UpdateDescription.StringFactory stringFactory = new GroupCallUpdateMessageFactory(context, joinedMembers, withTime, groupCallUpdateDetails);
return UpdateDescription.mentioning(joinedMembers, stringFactory, R.drawable.ic_video_16); return UpdateDescription.mentioning(joinedMembers, stringFactory, R.drawable.ic_video_16);
} }

View file

@ -12,7 +12,7 @@ import java.util.Objects;
public class CallParticipant { public class CallParticipant {
public static final CallParticipant EMPTY = createRemote(Recipient.UNKNOWN, null, new BroadcastVideoSink(null), false, false); public static final CallParticipant EMPTY = createRemote(Recipient.UNKNOWN, null, new BroadcastVideoSink(null), false, false, 0);
private final @NonNull CameraState cameraState; private final @NonNull CameraState cameraState;
private final @NonNull Recipient recipient; private final @NonNull Recipient recipient;
@ -20,6 +20,7 @@ public class CallParticipant {
private final @NonNull BroadcastVideoSink videoSink; private final @NonNull BroadcastVideoSink videoSink;
private final boolean videoEnabled; private final boolean videoEnabled;
private final boolean microphoneEnabled; private final boolean microphoneEnabled;
private final long lastSpoke;
public static @NonNull CallParticipant createLocal(@NonNull CameraState cameraState, public static @NonNull CallParticipant createLocal(@NonNull CameraState cameraState,
@NonNull BroadcastVideoSink renderer, @NonNull BroadcastVideoSink renderer,
@ -30,16 +31,18 @@ public class CallParticipant {
renderer, renderer,
cameraState, cameraState,
cameraState.isEnabled() && cameraState.getCameraCount() > 0, cameraState.isEnabled() && cameraState.getCameraCount() > 0,
microphoneEnabled); microphoneEnabled,
0);
} }
public static @NonNull CallParticipant createRemote(@NonNull Recipient recipient, public static @NonNull CallParticipant createRemote(@NonNull Recipient recipient,
@Nullable IdentityKey identityKey, @Nullable IdentityKey identityKey,
@NonNull BroadcastVideoSink renderer, @NonNull BroadcastVideoSink renderer,
boolean audioEnabled, boolean audioEnabled,
boolean videoEnabled) boolean videoEnabled,
long lastSpoke)
{ {
return new CallParticipant(recipient, identityKey, renderer, CameraState.UNKNOWN, videoEnabled, audioEnabled); return new CallParticipant(recipient, identityKey, renderer, CameraState.UNKNOWN, videoEnabled, audioEnabled, lastSpoke);
} }
private CallParticipant(@NonNull Recipient recipient, private CallParticipant(@NonNull Recipient recipient,
@ -47,7 +50,8 @@ public class CallParticipant {
@NonNull BroadcastVideoSink videoSink, @NonNull BroadcastVideoSink videoSink,
@NonNull CameraState cameraState, @NonNull CameraState cameraState,
boolean videoEnabled, boolean videoEnabled,
boolean microphoneEnabled) boolean microphoneEnabled,
long lastSpoke)
{ {
this.recipient = recipient; this.recipient = recipient;
this.identityKey = identityKey; this.identityKey = identityKey;
@ -55,14 +59,15 @@ public class CallParticipant {
this.cameraState = cameraState; this.cameraState = cameraState;
this.videoEnabled = videoEnabled; this.videoEnabled = videoEnabled;
this.microphoneEnabled = microphoneEnabled; this.microphoneEnabled = microphoneEnabled;
this.lastSpoke = lastSpoke;
} }
public @NonNull CallParticipant withIdentityKey(@NonNull IdentityKey identityKey) { public @NonNull CallParticipant withIdentityKey(@NonNull IdentityKey identityKey) {
return new CallParticipant(recipient, identityKey, videoSink, cameraState, videoEnabled, microphoneEnabled); return new CallParticipant(recipient, identityKey, videoSink, cameraState, videoEnabled, microphoneEnabled, lastSpoke);
} }
public @NonNull CallParticipant withVideoEnabled(boolean videoEnabled) { public @NonNull CallParticipant withVideoEnabled(boolean videoEnabled) {
return new CallParticipant(recipient, identityKey, videoSink, cameraState, videoEnabled, microphoneEnabled); return new CallParticipant(recipient, identityKey, videoSink, cameraState, videoEnabled, microphoneEnabled, lastSpoke);
} }
public @NonNull Recipient getRecipient() { public @NonNull Recipient getRecipient() {
@ -100,6 +105,10 @@ public class CallParticipant {
return cameraState.getCameraCount() > 1; return cameraState.getCameraCount() > 1;
} }
public long getLastSpoke() {
return lastSpoke;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;
@ -107,6 +116,7 @@ public class CallParticipant {
CallParticipant that = (CallParticipant) o; CallParticipant that = (CallParticipant) o;
return videoEnabled == that.videoEnabled && return videoEnabled == that.videoEnabled &&
microphoneEnabled == that.microphoneEnabled && microphoneEnabled == that.microphoneEnabled &&
lastSpoke == that.lastSpoke &&
cameraState.equals(that.cameraState) && cameraState.equals(that.cameraState) &&
recipient.equals(that.recipient) && recipient.equals(that.recipient) &&
Objects.equals(identityKey, that.identityKey) && Objects.equals(identityKey, that.identityKey) &&
@ -115,7 +125,7 @@ public class CallParticipant {
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(cameraState, recipient, identityKey, videoSink, videoEnabled, microphoneEnabled); return Objects.hash(cameraState, recipient, identityKey, videoSink, videoEnabled, microphoneEnabled, lastSpoke);
} }
@Override @Override

View file

@ -31,6 +31,7 @@ import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.TransactionTooLargeException; import android.os.TransactionTooLargeException;
import android.service.notification.StatusBarNotification; import android.service.notification.StatusBarNotification;
import android.text.SpannableString;
import android.text.SpannableStringBuilder; import android.text.SpannableStringBuilder;
import android.text.TextUtils; import android.text.TextUtils;
@ -607,6 +608,9 @@ public class DefaultMessageNotifier implements MessageNotifier {
body = ThreadBodyUtil.getFormattedBodyFor(context, record); body = ThreadBodyUtil.getFormattedBodyFor(context, record);
slideDeck = ((MmsMessageRecord) record).getSlideDeck(); slideDeck = ((MmsMessageRecord) record).getSlideDeck();
canReply = true; canReply = true;
} else if (record.isGroupCall()) {
body = new SpannableString(MessageRecord.getGroupCallUpdateDescription(context, record.getBody(), false).getString());
canReply = false;
} else { } else {
canReply = true; canReply = true;
} }

View file

@ -76,6 +76,7 @@ import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserExce
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -667,8 +668,6 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
public void sendGroupCallMessage(@NonNull Recipient recipient, @Nullable String groupCallEraId) { public void sendGroupCallMessage(@NonNull Recipient recipient, @Nullable String groupCallEraId) {
SignalExecutors.BOUNDED.execute(() -> ApplicationDependencies.getJobManager().add(GroupCallUpdateSendJob.create(recipient.getId(), groupCallEraId))); SignalExecutors.BOUNDED.execute(() -> ApplicationDependencies.getJobManager().add(GroupCallUpdateSendJob.create(recipient.getId(), groupCallEraId)));
peekGroupCall(new WebRtcData.GroupCallUpdateMetadata(Recipient.self().getId(), recipient.getId(), groupCallEraId, System.currentTimeMillis()));
} }
public void peekGroupCall(@NonNull WebRtcData.GroupCallUpdateMetadata groupCallUpdateMetadata) { public void peekGroupCall(@NonNull WebRtcData.GroupCallUpdateMetadata groupCallUpdateMetadata) {
@ -689,6 +688,9 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
groupCallUpdateMetadata.getGroupCallEraId(), groupCallUpdateMetadata.getGroupCallEraId(),
peekInfo.getEraId(), peekInfo.getEraId(),
peekInfo.getJoinedMembers()); peekInfo.getJoinedMembers());
long threadId = DatabaseFactory.getThreadDatabase(this).getThreadIdFor(group);
ApplicationDependencies.getMessageNotifier().updateNotification(this, threadId, true);
}); });
} catch (IOException | VerificationFailedException | CallException e) { } catch (IOException | VerificationFailedException | CallException e) {
@ -697,6 +699,15 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
}); });
} }
public void updateGroupCallUpdateMessage(@NonNull RecipientId groupId, @Nullable String groupCallEraId, @NonNull Collection<UUID> joinedMembers) {
DatabaseFactory.getSmsDatabase(this).insertOrUpdateGroupCall(groupId,
Recipient.self().getId(),
System.currentTimeMillis(),
null,
groupCallEraId,
joinedMembers);
}
@Override @Override
public void onStartCall(@Nullable Remote remote, @NonNull CallId callId, @NonNull Boolean isOutgoing, @Nullable CallManager.CallMediaType callMediaType) { public void onStartCall(@Nullable Remote remote, @NonNull CallId callId, @NonNull Boolean isOutgoing, @Nullable CallManager.CallMediaType callMediaType) {
Log.i(TAG, "onStartCall(): callId: " + callId + ", outgoing: " + isOutgoing + ", type: " + callMediaType); Log.i(TAG, "onStartCall(): callId: " + callId + ", outgoing: " + isOutgoing + ", type: " + callMediaType);

View file

@ -45,7 +45,8 @@ public class BeginCallActionProcessorDelegate extends WebRtcActionProcessor {
null, null,
new BroadcastVideoSink(currentState.getVideoState().getEglBase()), new BroadcastVideoSink(currentState.getVideoState().getEglBase()),
true, true,
false false,
0
)) ))
.build(); .build();
@ -84,7 +85,8 @@ public class BeginCallActionProcessorDelegate extends WebRtcActionProcessor {
null, null,
new BroadcastVideoSink(currentState.getVideoState().getEglBase()), new BroadcastVideoSink(currentState.getVideoState().getEglBase()),
true, true,
false false,
0
)) ))
.build(); .build();
} }

View file

@ -69,7 +69,8 @@ public class GroupActionProcessor extends DeviceAwareActionProcessor {
null, null,
videoSink, videoSink,
Boolean.FALSE.equals(device.getAudioMuted()), Boolean.FALSE.equals(device.getAudioMuted()),
Boolean.FALSE.equals(device.getVideoMuted()))); Boolean.FALSE.equals(device.getVideoMuted()),
device.getSpeakerTime()));
} }
return builder.build(); return builder.build();

View file

@ -2,14 +2,21 @@ package org.thoughtcrime.securesms.service.webrtc;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.annimon.stream.Stream;
import org.signal.ringrtc.CallException; import org.signal.ringrtc.CallException;
import org.signal.ringrtc.GroupCall; import org.signal.ringrtc.GroupCall;
import org.signal.ringrtc.PeekInfo; import org.signal.ringrtc.PeekInfo;
import org.thoughtcrime.securesms.events.WebRtcViewModel; import org.thoughtcrime.securesms.events.WebRtcViewModel;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.ringrtc.Camera; import org.thoughtcrime.securesms.ringrtc.Camera;
import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState; import org.thoughtcrime.securesms.service.webrtc.state.WebRtcServiceState;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/** /**
* Process actions for when the call has at least once been connected and joined. * Process actions for when the call has at least once been connected and joined.
*/ */
@ -100,7 +107,14 @@ public class GroupConnectedActionProcessor extends GroupActionProcessor {
return currentState; return currentState;
} }
webRtcInteractor.sendGroupCallMessage(currentState.getCallInfoState().getCallRecipient(), WebRtcUtil.getGroupCallEraId(groupCall)); String eraId = WebRtcUtil.getGroupCallEraId(groupCall);
webRtcInteractor.sendGroupCallMessage(currentState.getCallInfoState().getCallRecipient(), eraId);
List<UUID> members = new ArrayList<>(peekInfo.getJoinedMembers());
if (!members.contains(Recipient.self().requireUuid())) {
members.add(Recipient.self().requireUuid());
}
webRtcInteractor.updateGroupCallUpdateMessage(currentState.getCallInfoState().getCallRecipient().getId(), eraId, members);
return currentState.builder() return currentState.builder()
.changeCallSetupState() .changeCallSetupState()
@ -120,7 +134,11 @@ public class GroupConnectedActionProcessor extends GroupActionProcessor {
return groupCallFailure(currentState, "Unable to disconnect from group call", e); return groupCallFailure(currentState, "Unable to disconnect from group call", e);
} }
webRtcInteractor.sendGroupCallMessage(currentState.getCallInfoState().getCallRecipient(), WebRtcUtil.getGroupCallEraId(groupCall)); String eraId = WebRtcUtil.getGroupCallEraId(groupCall);
webRtcInteractor.sendGroupCallMessage(currentState.getCallInfoState().getCallRecipient(), eraId);
List<UUID> members = Stream.of(currentState.getCallInfoState().getRemoteCallParticipants()).map(p -> p.getRecipient().requireUuid()).toList();
webRtcInteractor.updateGroupCallUpdateMessage(currentState.getCallInfoState().getCallRecipient().getId(), eraId, members);
currentState = currentState.builder() currentState = currentState.builder()
.changeCallInfoState() .changeCallInfoState()

View file

@ -114,7 +114,7 @@ public class GroupPreJoinActionProcessor extends GroupActionProcessor {
.changeCallInfoState(); .changeCallInfoState();
for (Recipient recipient : callParticipants) { for (Recipient recipient : callParticipants) {
builder.putParticipant(recipient, CallParticipant.createRemote(recipient, null, new BroadcastVideoSink(null), true, true)); builder.putParticipant(recipient, CallParticipant.createRemote(recipient, null, new BroadcastVideoSink(null), true, true, 0));
} }
return builder.build(); return builder.build();

View file

@ -8,6 +8,7 @@ import androidx.annotation.Nullable;
import org.signal.ringrtc.CallManager; import org.signal.ringrtc.CallManager;
import org.signal.ringrtc.GroupCall; import org.signal.ringrtc.GroupCall;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.ringrtc.CameraEventListener; import org.thoughtcrime.securesms.ringrtc.CameraEventListener;
import org.thoughtcrime.securesms.ringrtc.RemotePeer; import org.thoughtcrime.securesms.ringrtc.RemotePeer;
import org.thoughtcrime.securesms.service.WebRtcCallService; import org.thoughtcrime.securesms.service.WebRtcCallService;
@ -18,6 +19,7 @@ import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager;
import org.thoughtcrime.securesms.webrtc.locks.LockManager; import org.thoughtcrime.securesms.webrtc.locks.LockManager;
import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage; import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage;
import java.util.Collection;
import java.util.UUID; import java.util.UUID;
/** /**
@ -92,6 +94,10 @@ public class WebRtcInteractor {
webRtcCallService.sendGroupCallMessage(recipient, groupCallEraId); webRtcCallService.sendGroupCallMessage(recipient, groupCallEraId);
} }
void updateGroupCallUpdateMessage(@NonNull RecipientId groupId, @Nullable String groupCallEraId, @NonNull Collection<UUID> joinedMembers) {
webRtcCallService.updateGroupCallUpdateMessage(groupId, groupCallEraId, joinedMembers);
}
void setCallInProgressNotification(int type, @NonNull RemotePeer remotePeer) { void setCallInProgressNotification(int type, @NonNull RemotePeer remotePeer) {
webRtcCallService.setCallInProgressNotification(type, remotePeer.getRecipient()); webRtcCallService.setCallInProgressNotification(type, remotePeer.getRecipient());
} }

View file

@ -1215,10 +1215,18 @@
<!-- Group Calling update messages --> <!-- Group Calling update messages -->
<string name="MessageRecord_s_started_a_group_call_s">%1$s started a group call · %2$s</string> <string name="MessageRecord_s_started_a_group_call_s">%1$s started a group call · %2$s</string>
<string name="MessageRecord_s_is_in_the_group_call_s">%1$s is in the group call · %2$s</string> <string name="MessageRecord_s_is_in_the_group_call_s">%1$s is in the group call · %2$s</string>
<string name="MessageRecord_you_are_in_the_group_call_s">You are in the group call · %2$s</string> <string name="MessageRecord_you_are_in_the_group_call_s1">You are in the group call · %1$s</string>
<string name="MessageRecord_s_and_s_are_in_the_group_call_s">%1$s and %2$s are in the group call · %3$s</string> <string name="MessageRecord_s_and_s_are_in_the_group_call_s1">%1$s and %2$s are in the group call · %3$s</string>
<string name="MessageRecord_s_s_and_s_are_in_the_group_call_s">%1$s, %2$s, and %3$s are in the group call · %4$s</string> <string name="MessageRecord_s_s_and_s_are_in_the_group_call_s">%1$s, %2$s, and %3$s are in the group call · %4$s</string>
<string name="MessageRecord_group_call_s">Group call · %1$s</string> <string name="MessageRecord_group_call_s">Group call · %1$s</string>
<string name="MessageRecord_s_started_a_group_call">%1$s started a group call</string>
<string name="MessageRecord_s_is_in_the_group_call">%1$s is in the group call</string>
<string name="MessageRecord_you_are_in_the_group_call">You are in the group call</string>
<string name="MessageRecord_s_and_s_are_in_the_group_call">%1$s and %2$s are in the group call</string>
<string name="MessageRecord_s_s_and_s_are_in_the_group_call">%1$s, %2$s, and %3$s are in the group call</string>
<string name="MessageRecord_group_call">Group call</string>
<string name="MessageRecord_you">You</string> <string name="MessageRecord_you">You</string>
<plurals name="MessageRecord_s_s_and_d_others_are_in_the_group_call_s"> <plurals name="MessageRecord_s_s_and_d_others_are_in_the_group_call_s">
@ -1226,6 +1234,11 @@
<item quantity="other">%1$s, %2$s, and %3$d others are in the group call · %4$s</item> <item quantity="other">%1$s, %2$s, and %3$d others are in the group call · %4$s</item>
</plurals> </plurals>
<plurals name="MessageRecord_s_s_and_d_others_are_in_the_group_call">
<item quantity="one">%1$s, %2$s, and %3$d other are in the group call</item>
<item quantity="other">%1$s, %2$s, and %3$d others are in the group call</item>
</plurals>
<!-- MessageRequestBottomView --> <!-- MessageRequestBottomView -->
<string name="MessageRequestBottomView_accept">Accept</string> <string name="MessageRequestBottomView_accept">Accept</string>
<string name="MessageRequestBottomView_continue">Continue</string> <string name="MessageRequestBottomView_continue">Continue</string>