Ensure proper group call history in chat after being offline.
Co-authored-by: Alan Evans <alan@signal.org>
This commit is contained in:
parent
12330b0aff
commit
dc4ce234b7
8 changed files with 282 additions and 13 deletions
|
@ -135,10 +135,13 @@ public abstract class MessageDatabase extends Database implements MmsSmsColumns
|
|||
public abstract void insertOrUpdateGroupCall(@NonNull RecipientId groupRecipientId,
|
||||
@NonNull RecipientId sender,
|
||||
long timestamp,
|
||||
@Nullable String messageGroupCallEraId,
|
||||
@Nullable String peekGroupCallEraId,
|
||||
@NonNull Collection<UUID> peekJoinedUuids,
|
||||
boolean isCallFull);
|
||||
public abstract void insertOrUpdateGroupCall(@NonNull RecipientId groupRecipientId,
|
||||
@NonNull RecipientId sender,
|
||||
long timestamp,
|
||||
@Nullable String messageGroupCallEraId);
|
||||
public abstract boolean updatePreviousGroupCall(long threadId, @Nullable String peekGroupCallEraId, @NonNull Collection<UUID> peekJoinedUuids, boolean isCallFull);
|
||||
|
||||
public abstract Optional<InsertResult> insertMessageInbox(IncomingTextMessage message, long type);
|
||||
|
|
|
@ -466,7 +466,6 @@ public class MmsDatabase extends MessageDatabase {
|
|||
public void insertOrUpdateGroupCall(@NonNull RecipientId groupRecipientId,
|
||||
@NonNull RecipientId sender,
|
||||
long timestamp,
|
||||
@Nullable String messageGroupCallEraId,
|
||||
@Nullable String peekGroupCallEraId,
|
||||
@NonNull Collection<UUID> peekJoinedUuids,
|
||||
boolean isCallFull)
|
||||
|
@ -474,6 +473,15 @@ public class MmsDatabase extends MessageDatabase {
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertOrUpdateGroupCall(@NonNull RecipientId groupRecipientId,
|
||||
@NonNull RecipientId sender,
|
||||
long timestamp,
|
||||
@Nullable String messageGroupCallEraId)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updatePreviousGroupCall(long threadId, @Nullable String peekGroupCallEraId, @NonNull Collection<UUID> peekJoinedUuids, boolean isCallFull) {
|
||||
throw new UnsupportedOperationException();
|
||||
|
|
|
@ -688,7 +688,6 @@ public class SmsDatabase extends MessageDatabase {
|
|||
public void insertOrUpdateGroupCall(@NonNull RecipientId groupRecipientId,
|
||||
@NonNull RecipientId sender,
|
||||
long timestamp,
|
||||
@Nullable String messageGroupCallEraId,
|
||||
@Nullable String peekGroupCallEraId,
|
||||
@NonNull Collection<UUID> peekJoinedUuids,
|
||||
boolean isCallFull)
|
||||
|
@ -747,6 +746,83 @@ public class SmsDatabase extends MessageDatabase {
|
|||
ApplicationDependencies.getJobManager().add(new TrimThreadJob(threadId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertOrUpdateGroupCall(@NonNull RecipientId groupRecipientId,
|
||||
@NonNull RecipientId sender,
|
||||
long timestamp,
|
||||
@Nullable String messageGroupCallEraId)
|
||||
{
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
|
||||
long threadId;
|
||||
|
||||
try {
|
||||
db.beginTransaction();
|
||||
|
||||
Recipient recipient = Recipient.resolved(groupRecipientId);
|
||||
|
||||
threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||
|
||||
String where = TYPE + " = ? AND " + THREAD_ID + " = ?";
|
||||
String[] args = SqlUtil.buildArgs(Types.GROUP_CALL_TYPE, threadId);
|
||||
boolean sameEraId = false;
|
||||
|
||||
try (Reader reader = new Reader(db.query(TABLE_NAME, MESSAGE_PROJECTION, where, args, null, null, DATE_RECEIVED + " DESC", "1"))) {
|
||||
MessageRecord record = reader.getNext();
|
||||
if (record != null) {
|
||||
GroupCallUpdateDetails groupCallUpdateDetails = GroupCallUpdateDetailsUtil.parse(record.getBody());
|
||||
|
||||
sameEraId = groupCallUpdateDetails.getEraId().equals(messageGroupCallEraId) && !Util.isEmpty(messageGroupCallEraId);
|
||||
|
||||
if (!sameEraId) {
|
||||
String body = GroupCallUpdateDetailsUtil.createUpdatedBody(groupCallUpdateDetails, Collections.emptyList(), false);
|
||||
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(BODY, body);
|
||||
|
||||
db.update(TABLE_NAME, contentValues, ID_WHERE, SqlUtil.buildArgs(record.getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!sameEraId && !Util.isEmpty(messageGroupCallEraId)) {
|
||||
byte[] updateDetails = GroupCallUpdateDetails.newBuilder()
|
||||
.setEraId(Util.emptyIfNull(messageGroupCallEraId))
|
||||
.setStartedCallUuid(Recipient.resolved(sender).requireUuid().toString())
|
||||
.setStartedCallTimestamp(timestamp)
|
||||
.addAllInCallUuids(Collections.emptyList())
|
||||
.setIsCallFull(false)
|
||||
.build()
|
||||
.toByteArray();
|
||||
|
||||
String body = Base64.encodeBytes(updateDetails);
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(RECIPIENT_ID, sender.serialize());
|
||||
values.put(ADDRESS_DEVICE_ID, 1);
|
||||
values.put(DATE_RECEIVED, timestamp);
|
||||
values.put(DATE_SENT, timestamp);
|
||||
values.put(READ, 0);
|
||||
values.put(BODY, body);
|
||||
values.put(TYPE, Types.GROUP_CALL_TYPE);
|
||||
values.put(THREAD_ID, threadId);
|
||||
|
||||
db.insert(TABLE_NAME, null, values);
|
||||
|
||||
DatabaseFactory.getThreadDatabase(context).incrementUnread(threadId, 1);
|
||||
}
|
||||
|
||||
DatabaseFactory.getThreadDatabase(context).update(threadId, true);
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
|
||||
notifyConversationListeners(threadId);
|
||||
ApplicationDependencies.getJobManager().add(new TrimThreadJob(threadId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updatePreviousGroupCall(long threadId, @Nullable String peekGroupCallEraId, @NonNull Collection<UUID> peekJoinedUuids, boolean isCallFull) {
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.WebsocketDrainedConstraint;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.service.webrtc.WebRtcData;
|
||||
|
||||
/**
|
||||
* Allows the enqueueing of one peek operation per group while the web socket is not drained.
|
||||
*/
|
||||
public final class GroupCallPeekJob extends BaseJob {
|
||||
|
||||
public static final String KEY = "GroupCallPeekJob";
|
||||
|
||||
private static final String QUEUE = "__GroupCallPeekJob__";
|
||||
|
||||
private static final String KEY_SENDER = "sender";
|
||||
private static final String KEY_GROUP_RECIPIENT_ID = "group_recipient_id";
|
||||
private static final String KEY_GROUP_CALL_ERA_ID = "group_call_era_id";
|
||||
private static final String KEY_SERVER_RECEIVED_TIMESTAMP = "server_timestamp";
|
||||
|
||||
@NonNull private final WebRtcData.GroupCallUpdateMetadata updateMetadata;
|
||||
|
||||
public static void enqueue(@NonNull WebRtcData.GroupCallUpdateMetadata updateMetadata) {
|
||||
JobManager jobManager = ApplicationDependencies.getJobManager();
|
||||
String queue = QUEUE + updateMetadata.getGroupRecipientId().serialize();
|
||||
Parameters.Builder parameters = new Parameters.Builder()
|
||||
.setQueue(queue)
|
||||
.addConstraint(WebsocketDrainedConstraint.KEY);
|
||||
|
||||
jobManager.cancelAllInQueue(queue);
|
||||
|
||||
jobManager.add(new GroupCallPeekJob(parameters.build(), updateMetadata));
|
||||
}
|
||||
|
||||
private GroupCallPeekJob(@NonNull Parameters parameters,
|
||||
@NonNull WebRtcData.GroupCallUpdateMetadata updateMetadata)
|
||||
{
|
||||
super(parameters);
|
||||
this.updateMetadata = updateMetadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRun() {
|
||||
ApplicationDependencies.getJobManager().add(new GroupCallPeekWorkerJob(updateMetadata));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onShouldRetry(@NonNull Exception e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Data serialize() {
|
||||
return new Data.Builder()
|
||||
.putString(KEY_SENDER, updateMetadata.getSender().serialize())
|
||||
.putString(KEY_GROUP_RECIPIENT_ID, updateMetadata.getGroupRecipientId().serialize())
|
||||
.putString(KEY_GROUP_CALL_ERA_ID, updateMetadata.getGroupCallEraId())
|
||||
.putLong(KEY_SERVER_RECEIVED_TIMESTAMP, updateMetadata.getServerReceivedTimestamp())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String getFactoryKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
}
|
||||
|
||||
public static final class Factory implements Job.Factory<GroupCallPeekJob> {
|
||||
|
||||
@Override
|
||||
public @NonNull GroupCallPeekJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
||||
RecipientId sender = RecipientId.from(data.getString(KEY_SENDER));
|
||||
RecipientId group = RecipientId.from(data.getString(KEY_GROUP_RECIPIENT_ID));
|
||||
String era = data.getString(KEY_GROUP_CALL_ERA_ID);
|
||||
long serverTimestamp = data.getLong(KEY_SERVER_RECEIVED_TIMESTAMP);
|
||||
|
||||
return new GroupCallPeekJob(parameters, new WebRtcData.GroupCallUpdateMetadata(sender, group, era, serverTimestamp));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.service.WebRtcCallService;
|
||||
import org.thoughtcrime.securesms.service.webrtc.WebRtcData;
|
||||
|
||||
/**
|
||||
* Runs in the same queue as messages for the group.
|
||||
*/
|
||||
final class GroupCallPeekWorkerJob extends BaseJob {
|
||||
|
||||
public static final String KEY = "GroupCallPeekWorkerJob";
|
||||
|
||||
private static final String KEY_SENDER = "sender";
|
||||
private static final String KEY_GROUP_RECIPIENT_ID = "group_recipient_id";
|
||||
private static final String KEY_GROUP_CALL_ERA_ID = "group_call_era_id";
|
||||
private static final String KEY_SERVER_RECEIVED_TIMESTAMP = "server_timestamp";
|
||||
|
||||
@NonNull private final WebRtcData.GroupCallUpdateMetadata updateMetadata;
|
||||
|
||||
public GroupCallPeekWorkerJob(@NonNull WebRtcData.GroupCallUpdateMetadata updateMetadata) {
|
||||
this(new Parameters.Builder()
|
||||
.setQueue(PushProcessMessageJob.getQueueName(updateMetadata.getGroupRecipientId()))
|
||||
.build(),
|
||||
updateMetadata);
|
||||
}
|
||||
|
||||
private GroupCallPeekWorkerJob(@NonNull Parameters parameters,
|
||||
@NonNull WebRtcData.GroupCallUpdateMetadata updateMetadata)
|
||||
{
|
||||
super(parameters);
|
||||
this.updateMetadata = updateMetadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRun() {
|
||||
Intent intent = new Intent(context, WebRtcCallService.class);
|
||||
|
||||
intent.setAction(WebRtcCallService.ACTION_GROUP_CALL_UPDATE_MESSAGE)
|
||||
.putExtra(WebRtcCallService.EXTRA_GROUP_CALL_UPDATE_SENDER, updateMetadata.getSender().serialize())
|
||||
.putExtra(WebRtcCallService.EXTRA_GROUP_CALL_UPDATE_GROUP, updateMetadata.getGroupRecipientId().serialize())
|
||||
.putExtra(WebRtcCallService.EXTRA_GROUP_CALL_ERA_ID, updateMetadata.getGroupCallEraId())
|
||||
.putExtra(WebRtcCallService.EXTRA_SERVER_RECEIVED_TIMESTAMP, updateMetadata.getServerReceivedTimestamp());
|
||||
|
||||
context.startService(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onShouldRetry(@NonNull Exception e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Data serialize() {
|
||||
return new Data.Builder()
|
||||
.putString(KEY_SENDER, updateMetadata.getSender().serialize())
|
||||
.putString(KEY_GROUP_RECIPIENT_ID, updateMetadata.getGroupRecipientId().serialize())
|
||||
.putString(KEY_GROUP_CALL_ERA_ID, updateMetadata.getGroupCallEraId())
|
||||
.putLong(KEY_SERVER_RECEIVED_TIMESTAMP, updateMetadata.getServerReceivedTimestamp())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String getFactoryKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure() {
|
||||
}
|
||||
|
||||
public static final class Factory implements Job.Factory<GroupCallPeekWorkerJob> {
|
||||
|
||||
@Override
|
||||
public @NonNull GroupCallPeekWorkerJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
||||
RecipientId sender = RecipientId.from(data.getString(KEY_SENDER));
|
||||
RecipientId group = RecipientId.from(data.getString(KEY_GROUP_RECIPIENT_ID));
|
||||
String era = data.getString(KEY_GROUP_CALL_ERA_ID);
|
||||
long serverTimestamp = data.getLong(KEY_SERVER_RECEIVED_TIMESTAMP);
|
||||
|
||||
return new GroupCallPeekWorkerJob(parameters, new WebRtcData.GroupCallUpdateMetadata(sender, group, era, serverTimestamp));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -72,6 +72,8 @@ public final class JobManagerFactories {
|
|||
put(FcmRefreshJob.KEY, new FcmRefreshJob.Factory());
|
||||
put(GroupV1MigrationJob.KEY, new GroupV1MigrationJob.Factory());
|
||||
put(GroupCallUpdateSendJob.KEY, new GroupCallUpdateSendJob.Factory());
|
||||
put(GroupCallPeekJob.KEY, new GroupCallPeekJob.Factory());
|
||||
put(GroupCallPeekWorkerJob.KEY, new GroupCallPeekWorkerJob.Factory());
|
||||
put(KbsEnclaveMigrationWorkerJob.KEY, new KbsEnclaveMigrationWorkerJob.Factory());
|
||||
put(LeaveGroupJob.KEY, new LeaveGroupJob.Factory());
|
||||
put(LocalBackupJob.KEY, new LocalBackupJob.Factory());
|
||||
|
|
|
@ -74,6 +74,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
|
|||
import org.thoughtcrime.securesms.ringrtc.IceCandidateParcel;
|
||||
import org.thoughtcrime.securesms.ringrtc.RemotePeer;
|
||||
import org.thoughtcrime.securesms.service.WebRtcCallService;
|
||||
import org.thoughtcrime.securesms.service.webrtc.WebRtcData;
|
||||
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
|
||||
import org.thoughtcrime.securesms.sms.IncomingEndSessionMessage;
|
||||
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
|
||||
|
@ -661,17 +662,19 @@ public final class PushProcessMessageJob extends BaseJob {
|
|||
return;
|
||||
}
|
||||
|
||||
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromPossiblyMigratedGroupId(groupId.get());
|
||||
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromPossiblyMigratedGroupId(groupId.get());
|
||||
WebRtcData.GroupCallUpdateMetadata updateMetadata = new WebRtcData.GroupCallUpdateMetadata(RecipientId.from(content.getSender()),
|
||||
groupRecipientId,
|
||||
message.getGroupCallUpdate().get().getEraId(),
|
||||
content.getServerReceivedTimestamp());
|
||||
|
||||
Intent intent = new Intent(context, WebRtcCallService.class);
|
||||
DatabaseFactory.getSmsDatabase(context).insertOrUpdateGroupCall(updateMetadata.getGroupRecipientId(),
|
||||
updateMetadata.getSender(),
|
||||
updateMetadata.getServerReceivedTimestamp(),
|
||||
updateMetadata.getGroupCallEraId());
|
||||
|
||||
intent.setAction(WebRtcCallService.ACTION_GROUP_CALL_UPDATE_MESSAGE)
|
||||
.putExtra(WebRtcCallService.EXTRA_GROUP_CALL_UPDATE_SENDER, RecipientId.from(content.getSender()).serialize())
|
||||
.putExtra(WebRtcCallService.EXTRA_GROUP_CALL_UPDATE_GROUP, groupRecipientId.serialize())
|
||||
.putExtra(WebRtcCallService.EXTRA_GROUP_CALL_ERA_ID, message.getGroupCallUpdate().get().getEraId())
|
||||
.putExtra(WebRtcCallService.EXTRA_SERVER_RECEIVED_TIMESTAMP, content.getServerReceivedTimestamp());
|
||||
|
||||
context.startService(intent);
|
||||
GroupCallPeekJob.enqueue(updateMetadata);
|
||||
}
|
||||
|
||||
private void handleEndSessionMessage(@NonNull SignalServiceContent content,
|
||||
|
|
|
@ -715,7 +715,6 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
|
|||
DatabaseFactory.getSmsDatabase(this).insertOrUpdateGroupCall(group.getId(),
|
||||
groupCallUpdateMetadata.getSender(),
|
||||
groupCallUpdateMetadata.getServerReceivedTimestamp(),
|
||||
groupCallUpdateMetadata.getGroupCallEraId(),
|
||||
peekInfo.getEraId(),
|
||||
peekInfo.getJoinedMembers(),
|
||||
WebRtcUtil.isCallFull(peekInfo));
|
||||
|
@ -766,7 +765,6 @@ public class WebRtcCallService extends Service implements CallManager.Observer,
|
|||
SignalExecutors.BOUNDED.execute(() -> DatabaseFactory.getSmsDatabase(this).insertOrUpdateGroupCall(groupId,
|
||||
Recipient.self().getId(),
|
||||
System.currentTimeMillis(),
|
||||
null,
|
||||
groupCallEraId,
|
||||
joinedMembers,
|
||||
isCallFull));
|
||||
|
|
Loading…
Add table
Reference in a new issue