From a640d9e29850bf523a92c5b0dd460adbc1a89149 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Tue, 24 Nov 2020 13:23:25 -0500 Subject: [PATCH] Improve group update copy and implement speaker indexing. --- .../webrtc/CallParticipantsState.java | 7 ++- .../conversation/ConversationUpdateItem.java | 2 +- .../ConversationListItem.java | 2 +- .../securesms/database/SmsDatabase.java | 20 +++++--- .../model/GroupCallUpdateMessageFactory.java | 48 +++++++++++++------ .../database/model/MessageRecord.java | 7 ++- .../securesms/events/CallParticipant.java | 36 +++++++++----- .../notifications/DefaultMessageNotifier.java | 4 ++ .../securesms/service/WebRtcCallService.java | 15 +++++- .../BeginCallActionProcessorDelegate.java | 6 ++- .../service/webrtc/GroupActionProcessor.java | 3 +- .../webrtc/GroupConnectedActionProcessor.java | 22 ++++++++- .../webrtc/GroupPreJoinActionProcessor.java | 2 +- .../service/webrtc/WebRtcInteractor.java | 6 +++ app/src/main/res/values/strings.xml | 17 ++++++- 15 files changed, 145 insertions(+), 52 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsState.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsState.java index 8aa4664b7c..245d061bc4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsState.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/CallParticipantsState.java @@ -5,12 +5,12 @@ import android.content.Context; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.annimon.stream.ComparatorCompat; import com.annimon.stream.Stream; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.events.CallParticipant; import org.thoughtcrime.securesms.events.WebRtcViewModel; -import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.ringrtc.CameraState; import java.util.ArrayList; @@ -171,7 +171,10 @@ public final class CallParticipantsState { webRtcViewModel.getRemoteParticipants().size(), oldState.isViewingFocusedParticipant); - CallParticipant focused = oldState.remoteParticipants.isEmpty() ? null : oldState.remoteParticipants.get(0); + List 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(), webRtcViewModel.getGroupState(), diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java index fb4e597235..272a6dedde 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java @@ -183,7 +183,7 @@ public final class ConversationUpdateItem extends LinearLayout }); } else if (conversationMessage.getMessageRecord().isGroupCall()) { - UpdateDescription updateDescription = MessageRecord.getGroupCallUpdateDescription(getContext(), conversationMessage.getMessageRecord().getBody()); + UpdateDescription updateDescription = MessageRecord.getGroupCallUpdateDescription(getContext(), conversationMessage.getMessageRecord().getBody(), true); Collection uuids = updateDescription.getMentioned(); int text = 0; diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java index 15b9a95bc3..7d993208a6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java @@ -462,7 +462,7 @@ public final class ConversationListItem extends RelativeLayout } else if (SmsDatabase.Types.isMissedVideoCall(thread.getType())) { return emphasisAdded(context, context.getString(R.string.ThreadRecord_missed_video_call), defaultTint); } 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())) { 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())) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java index f577b62929..c0656302d3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -687,11 +687,11 @@ public class SmsDatabase extends MessageDatabase { @Override public void insertOrUpdateGroupCall(@NonNull RecipientId groupRecipientId, - @NonNull RecipientId sender, - long timestamp, - @Nullable String messageGroupCallEraId, - @Nullable String peekGroupCallEraId, - @NonNull Collection peekJoinedUuids) + @NonNull RecipientId sender, + long timestamp, + @Nullable String messageGroupCallEraId, + @Nullable String peekGroupCallEraId, + @NonNull Collection peekJoinedUuids) { SQLiteDatabase db = databaseHelper.getWritableDatabase(); @@ -704,6 +704,9 @@ public class SmsDatabase extends MessageDatabase { boolean peerEraIdSameAsPrevious = updatePreviousGroupCall(threadId, peekGroupCallEraId, peekJoinedUuids); if (!peerEraIdSameAsPrevious && !Util.isEmpty(peekGroupCallEraId)) { + Recipient self = Recipient.self(); + boolean markRead = peekJoinedUuids.contains(self.requireUuid()) || self.getId().equals(sender); + byte[] updateDetails = GroupCallUpdateDetails.newBuilder() .setEraId(Util.emptyIfNull(peekGroupCallEraId)) .setStartedCallUuid(Recipient.resolved(sender).requireUuid().toString()) @@ -719,7 +722,7 @@ public class SmsDatabase extends MessageDatabase { values.put(ADDRESS_DEVICE_ID, 1); values.put(DATE_RECEIVED, timestamp); values.put(DATE_SENT, timestamp); - values.put(READ, 0); + values.put(READ, markRead ? 1 : 0); values.put(BODY, body); values.put(TYPE, Types.GROUP_CALL_TYPE); values.put(THREAD_ID, threadId); @@ -753,6 +756,7 @@ public class SmsDatabase extends MessageDatabase { GroupCallUpdateDetails groupCallUpdateDetails = GroupCallUpdateDetailsUtil.parse(record.getBody()); boolean sameEraId = groupCallUpdateDetails.getEraId().equals(peekGroupCallEraId) && !Util.isEmpty(peekGroupCallEraId); + boolean containsSelf = peekJoinedUuids.contains(Recipient.self().requireUuid()); List inCallUuids = sameEraId ? Stream.of(peekJoinedUuids).map(UUID::toString).toList() : Collections.emptyList(); @@ -762,6 +766,10 @@ public class SmsDatabase extends MessageDatabase { ContentValues contentValues = new ContentValues(); contentValues.put(BODY, body); + if (sameEraId && containsSelf) { + contentValues.put(READ, 1); + } + db.update(TABLE_NAME, contentValues, ID_WHERE, SqlUtil.buildArgs(record.getId())); return sameEraId; diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupCallUpdateMessageFactory.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupCallUpdateMessageFactory.java index a3844967e7..68e4e164c1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupCallUpdateMessageFactory.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupCallUpdateMessageFactory.java @@ -25,12 +25,18 @@ import java.util.UUID; public class GroupCallUpdateMessageFactory implements UpdateDescription.StringFactory { private final Context context; private final List joinedMembers; + private final boolean withTime; private final GroupCallUpdateDetails groupCallUpdateDetails; private final UUID selfUuid; - public GroupCallUpdateMessageFactory(@NonNull Context context, @NonNull List joinedMembers, @NonNull GroupCallUpdateDetails groupCallUpdateDetails) { + public GroupCallUpdateMessageFactory(@NonNull Context context, + @NonNull List joinedMembers, + boolean withTime, + @NonNull GroupCallUpdateDetails groupCallUpdateDetails) + { this.context = context; this.joinedMembers = new ArrayList<>(joinedMembers); + this.withTime = withTime; this.groupCallUpdateDetails = groupCallUpdateDetails; this.selfUuid = TextSecurePreferences.getLocalUuid(context); @@ -46,28 +52,40 @@ public class GroupCallUpdateMessageFactory implements UpdateDescription.StringFa switch (joinedMembers.size()) { 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: 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)) { - 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 { - 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: - return context.getString(R.string.MessageRecord_s_and_s_are_in_the_group_call_s, - describe(joinedMembers.get(0)), - describe(joinedMembers.get(1)), - time); + return withTime ? context.getString(R.string.MessageRecord_s_and_s_are_in_the_group_call_s1, + describe(joinedMembers.get(0)), + describe(joinedMembers.get(1)), + time) + : context.getString(R.string.MessageRecord_s_and_s_are_in_the_group_call, + describe(joinedMembers.get(0)), + describe(joinedMembers.get(1))); default: int others = joinedMembers.size() - 2; - return context.getResources().getQuantityString(R.plurals.MessageRecord_s_s_and_d_others_are_in_the_group_call_s, - others, - describe(joinedMembers.get(0)), - describe(joinedMembers.get(1)), - others, - time); + return withTime ? context.getResources().getQuantityString(R.plurals.MessageRecord_s_s_and_d_others_are_in_the_group_call_s, + others, + describe(joinedMembers.get(0)), + describe(joinedMembers.get(1)), + others, + 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); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java index 9c53b84a2d..a0a2989540 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java @@ -162,7 +162,7 @@ public abstract class MessageRecord extends DisplayRecord { } 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)); } else if (isGroupCall()) { - return getGroupCallUpdateDescription(context, getBody()); + return getGroupCallUpdateDescription(context, getBody(), true); } else if (isJoined()) { return staticUpdateDescription(context.getString(R.string.MessageRecord_s_joined_signal, getIndividualRecipient().getDisplayName(context)), R.drawable.ic_update_group_add_16); } else if (isExpirationTimerUpdate()) { @@ -308,8 +308,7 @@ public abstract class MessageRecord extends DisplayRecord { } } - - public static @NonNull UpdateDescription getGroupCallUpdateDescription(@NonNull Context context, @NonNull String body) { + public static @NonNull UpdateDescription getGroupCallUpdateDescription(@NonNull Context context, @NonNull String body, boolean withTime) { GroupCallUpdateDetails groupCallUpdateDetails = GroupCallUpdateDetailsUtil.parse(body); List joinedMembers = Stream.of(groupCallUpdateDetails.getInCallUuidsList()) @@ -317,7 +316,7 @@ public abstract class MessageRecord extends DisplayRecord { .withoutNulls() .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); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/events/CallParticipant.java b/app/src/main/java/org/thoughtcrime/securesms/events/CallParticipant.java index db7315a6b7..2bc5860a1a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/events/CallParticipant.java +++ b/app/src/main/java/org/thoughtcrime/securesms/events/CallParticipant.java @@ -12,7 +12,7 @@ import java.util.Objects; 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 Recipient recipient; @@ -20,6 +20,7 @@ public class CallParticipant { private final @NonNull BroadcastVideoSink videoSink; private final boolean videoEnabled; private final boolean microphoneEnabled; + private final long lastSpoke; public static @NonNull CallParticipant createLocal(@NonNull CameraState cameraState, @NonNull BroadcastVideoSink renderer, @@ -30,16 +31,18 @@ public class CallParticipant { renderer, cameraState, cameraState.isEnabled() && cameraState.getCameraCount() > 0, - microphoneEnabled); + microphoneEnabled, + 0); } public static @NonNull CallParticipant createRemote(@NonNull Recipient recipient, @Nullable IdentityKey identityKey, @NonNull BroadcastVideoSink renderer, 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, @@ -47,7 +50,8 @@ public class CallParticipant { @NonNull BroadcastVideoSink videoSink, @NonNull CameraState cameraState, boolean videoEnabled, - boolean microphoneEnabled) + boolean microphoneEnabled, + long lastSpoke) { this.recipient = recipient; this.identityKey = identityKey; @@ -55,14 +59,15 @@ public class CallParticipant { this.cameraState = cameraState; this.videoEnabled = videoEnabled; this.microphoneEnabled = microphoneEnabled; + this.lastSpoke = lastSpoke; } 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) { - return new CallParticipant(recipient, identityKey, videoSink, cameraState, videoEnabled, microphoneEnabled); + return new CallParticipant(recipient, identityKey, videoSink, cameraState, videoEnabled, microphoneEnabled, lastSpoke); } public @NonNull Recipient getRecipient() { @@ -100,22 +105,27 @@ public class CallParticipant { return cameraState.getCameraCount() > 1; } + public long getLastSpoke() { + return lastSpoke; + } + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CallParticipant that = (CallParticipant) o; return videoEnabled == that.videoEnabled && - microphoneEnabled == that.microphoneEnabled && - cameraState.equals(that.cameraState) && - recipient.equals(that.recipient) && - Objects.equals(identityKey, that.identityKey) && - Objects.equals(videoSink, that.videoSink); + microphoneEnabled == that.microphoneEnabled && + lastSpoke == that.lastSpoke && + cameraState.equals(that.cameraState) && + recipient.equals(that.recipient) && + Objects.equals(identityKey, that.identityKey) && + Objects.equals(videoSink, that.videoSink); } @Override public int hashCode() { - return Objects.hash(cameraState, recipient, identityKey, videoSink, videoEnabled, microphoneEnabled); + return Objects.hash(cameraState, recipient, identityKey, videoSink, videoEnabled, microphoneEnabled, lastSpoke); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java index 70b92c1616..badca09e9c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java @@ -31,6 +31,7 @@ import android.net.Uri; import android.os.Build; import android.os.TransactionTooLargeException; import android.service.notification.StatusBarNotification; +import android.text.SpannableString; import android.text.SpannableStringBuilder; import android.text.TextUtils; @@ -607,6 +608,9 @@ public class DefaultMessageNotifier implements MessageNotifier { body = ThreadBodyUtil.getFormattedBodyFor(context, record); slideDeck = ((MmsMessageRecord) record).getSlideDeck(); canReply = true; + } else if (record.isGroupCall()) { + body = new SpannableString(MessageRecord.getGroupCallUpdateDescription(context, record.getBody(), false).getString()); + canReply = false; } else { canReply = true; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java b/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java index 78ea6e1cb1..f9ea2b04c5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java @@ -76,6 +76,7 @@ import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserExce import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; 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) { 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) { @@ -689,6 +688,9 @@ public class WebRtcCallService extends Service implements CallManager.Observer, groupCallUpdateMetadata.getGroupCallEraId(), peekInfo.getEraId(), peekInfo.getJoinedMembers()); + + long threadId = DatabaseFactory.getThreadDatabase(this).getThreadIdFor(group); + ApplicationDependencies.getMessageNotifier().updateNotification(this, threadId, true); }); } 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 joinedMembers) { + DatabaseFactory.getSmsDatabase(this).insertOrUpdateGroupCall(groupId, + Recipient.self().getId(), + System.currentTimeMillis(), + null, + groupCallEraId, + joinedMembers); + } + @Override 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); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/BeginCallActionProcessorDelegate.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/BeginCallActionProcessorDelegate.java index 17c4a25da6..43db0bb7ee 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/BeginCallActionProcessorDelegate.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/BeginCallActionProcessorDelegate.java @@ -45,7 +45,8 @@ public class BeginCallActionProcessorDelegate extends WebRtcActionProcessor { null, new BroadcastVideoSink(currentState.getVideoState().getEglBase()), true, - false + false, + 0 )) .build(); @@ -84,7 +85,8 @@ public class BeginCallActionProcessorDelegate extends WebRtcActionProcessor { null, new BroadcastVideoSink(currentState.getVideoState().getEglBase()), true, - false + false, + 0 )) .build(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupActionProcessor.java index 6f903ad239..ae5b15c740 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupActionProcessor.java @@ -69,7 +69,8 @@ public class GroupActionProcessor extends DeviceAwareActionProcessor { null, videoSink, Boolean.FALSE.equals(device.getAudioMuted()), - Boolean.FALSE.equals(device.getVideoMuted()))); + Boolean.FALSE.equals(device.getVideoMuted()), + device.getSpeakerTime())); } return builder.build(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupConnectedActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupConnectedActionProcessor.java index 173bf8a704..36586679b9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupConnectedActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupConnectedActionProcessor.java @@ -2,14 +2,21 @@ package org.thoughtcrime.securesms.service.webrtc; import androidx.annotation.NonNull; +import com.annimon.stream.Stream; + import org.signal.ringrtc.CallException; import org.signal.ringrtc.GroupCall; import org.signal.ringrtc.PeekInfo; import org.thoughtcrime.securesms.events.WebRtcViewModel; import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.ringrtc.Camera; 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. */ @@ -100,7 +107,14 @@ public class GroupConnectedActionProcessor extends GroupActionProcessor { return currentState; } - webRtcInteractor.sendGroupCallMessage(currentState.getCallInfoState().getCallRecipient(), WebRtcUtil.getGroupCallEraId(groupCall)); + String eraId = WebRtcUtil.getGroupCallEraId(groupCall); + webRtcInteractor.sendGroupCallMessage(currentState.getCallInfoState().getCallRecipient(), eraId); + + List 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() .changeCallSetupState() @@ -120,7 +134,11 @@ public class GroupConnectedActionProcessor extends GroupActionProcessor { 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 members = Stream.of(currentState.getCallInfoState().getRemoteCallParticipants()).map(p -> p.getRecipient().requireUuid()).toList(); + webRtcInteractor.updateGroupCallUpdateMessage(currentState.getCallInfoState().getCallRecipient().getId(), eraId, members); currentState = currentState.builder() .changeCallInfoState() 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 989ef069ae..09ac2e0b75 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 @@ -114,7 +114,7 @@ public class GroupPreJoinActionProcessor extends GroupActionProcessor { .changeCallInfoState(); 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(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java index 231a60b62f..0764975b9f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java @@ -8,6 +8,7 @@ import androidx.annotation.Nullable; import org.signal.ringrtc.CallManager; import org.signal.ringrtc.GroupCall; import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.ringrtc.CameraEventListener; import org.thoughtcrime.securesms.ringrtc.RemotePeer; 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.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage; +import java.util.Collection; import java.util.UUID; /** @@ -92,6 +94,10 @@ public class WebRtcInteractor { webRtcCallService.sendGroupCallMessage(recipient, groupCallEraId); } + void updateGroupCallUpdateMessage(@NonNull RecipientId groupId, @Nullable String groupCallEraId, @NonNull Collection joinedMembers) { + webRtcCallService.updateGroupCallUpdateMessage(groupId, groupCallEraId, joinedMembers); + } + void setCallInProgressNotification(int type, @NonNull RemotePeer remotePeer) { webRtcCallService.setCallInProgressNotification(type, remotePeer.getRecipient()); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e9fb515e5d..d21d96e291 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1215,10 +1215,18 @@ %1$s started a group call · %2$s %1$s is in the group call · %2$s - You are in the group call · %2$s - %1$s and %2$s are in the group call · %3$s + You are in the group call · %1$s + %1$s and %2$s are in the group call · %3$s %1$s, %2$s, and %3$s are in the group call · %4$s Group call · %1$s + + %1$s started a group call + %1$s is in the group call + You are in the group call + %1$s and %2$s are in the group call + %1$s, %2$s, and %3$s are in the group call + Group call + You @@ -1226,6 +1234,11 @@ %1$s, %2$s, and %3$d others are in the group call · %4$s + + %1$s, %2$s, and %3$d other are in the group call + %1$s, %2$s, and %3$d others are in the group call + + Accept Continue