Improve group update copy and implement speaker indexing.
This commit is contained in:
parent
ce68da1613
commit
a640d9e298
15 changed files with 145 additions and 52 deletions
|
@ -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(),
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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())) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Add table
Reference in a new issue