diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/links/UpdateCallLinkRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/links/UpdateCallLinkRepository.kt index 4eb9debe68..8f2f21cca6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/links/UpdateCallLinkRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/links/UpdateCallLinkRepository.kt @@ -10,6 +10,7 @@ import io.reactivex.rxjava3.schedulers.Schedulers import org.signal.ringrtc.CallLinkState import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobs.CallLinkUpdateSendJob import org.thoughtcrime.securesms.service.webrtc.links.CallLinkCredentials import org.thoughtcrime.securesms.service.webrtc.links.SignalCallLinkManager import org.thoughtcrime.securesms.service.webrtc.links.UpdateCallLinkResult @@ -58,6 +59,7 @@ class UpdateCallLinkRepository( return { result -> if (result is UpdateCallLinkResult.Success) { SignalDatabase.callLinks.updateCallLinkState(credentials.roomId, result.state) + ApplicationDependencies.getJobManager().add(CallLinkUpdateSendJob(credentials.roomId)) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogRepository.kt index fb6a345e22..5824ab3430 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogRepository.kt @@ -5,11 +5,14 @@ import io.reactivex.rxjava3.core.Observable import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.schedulers.Schedulers import org.signal.core.util.concurrent.SignalExecutors +import org.signal.core.util.withinTransaction import org.thoughtcrime.securesms.calls.links.UpdateCallLinkRepository +import org.thoughtcrime.securesms.database.CallLinkTable import org.thoughtcrime.securesms.database.DatabaseObserver import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import org.thoughtcrime.securesms.jobs.CallLinkPeekJob +import org.thoughtcrime.securesms.jobs.CallLogEventSendJob import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId import org.thoughtcrime.securesms.service.webrtc.links.UpdateCallLinkResult @@ -80,6 +83,23 @@ class CallLogRepository( }.subscribeOn(Schedulers.io()) } + /** + * Delete all call events / unowned links and enqueue clear history job, and then + * emit a clear history message. + */ + fun deleteAllCallLogsOnOrBeforeNow(): Single { + return Single.fromCallable { + SignalDatabase.rawDatabase.withinTransaction { + val now = System.currentTimeMillis() + SignalDatabase.calls.deleteNonAdHocCallEventsOnOrBefore(now) + SignalDatabase.callLinks.deleteNonAdminCallLinksOnOrBefore(now) + ApplicationDependencies.getJobManager().add(CallLogEventSendJob.forClearHistory(now)) + } + + SignalDatabase.callLinks.getAllAdminCallLinksExcept(emptySet()) + }.flatMap(this::revokeAndCollectResults).map { -1 }.subscribeOn(Schedulers.io()) + } + /** * Deletes the selected call links. We DELETE those links we don't have admin keys for, * and revoke the ones we *do* have admin keys for. We then perform a cleanup step on @@ -93,19 +113,7 @@ class CallLogRepository( val allCallLinkIds = SignalDatabase.calls.getCallLinkRoomIdsFromCallRowIds(selectedCallRowIds) + selectedRoomIds SignalDatabase.callLinks.deleteNonAdminCallLinks(allCallLinkIds) SignalDatabase.callLinks.getAdminCallLinks(allCallLinkIds) - }.flatMap { callLinksToRevoke -> - Single.merge( - callLinksToRevoke.map { - updateCallLinkRepository.revokeCallLink(it.credentials!!) - } - ).reduce(0) { acc, current -> - acc + (if (current is UpdateCallLinkResult.Success) 0 else 1) - } - }.doOnTerminate { - SignalDatabase.calls.updateAdHocCallEventDeletionTimestamps() - }.doOnDispose { - SignalDatabase.calls.updateAdHocCallEventDeletionTimestamps() - }.subscribeOn(Schedulers.io()) + }.flatMap(this::revokeAndCollectResults).subscribeOn(Schedulers.io()) } /** @@ -121,19 +129,21 @@ class CallLogRepository( val allCallLinkIds = SignalDatabase.calls.getCallLinkRoomIdsFromCallRowIds(selectedCallRowIds) + selectedRoomIds SignalDatabase.callLinks.deleteAllNonAdminCallLinksExcept(allCallLinkIds) SignalDatabase.callLinks.getAllAdminCallLinksExcept(allCallLinkIds) - }.flatMap { callLinksToRevoke -> - Single.merge( - callLinksToRevoke.map { - updateCallLinkRepository.revokeCallLink(it.credentials!!) - } - ).reduce(0) { acc, current -> - acc + (if (current is UpdateCallLinkResult.Success) 0 else 1) + }.flatMap(this::revokeAndCollectResults).subscribeOn(Schedulers.io()) + } + + private fun revokeAndCollectResults(callLinksToRevoke: Set): Single { + return Single.merge( + callLinksToRevoke.map { + updateCallLinkRepository.revokeCallLink(it.credentials!!) } + ).reduce(0) { acc, current -> + acc + (if (current is UpdateCallLinkResult.Success) 0 else 1) }.doOnTerminate { SignalDatabase.calls.updateAdHocCallEventDeletionTimestamps() }.doOnDispose { SignalDatabase.calls.updateAdHocCallEventDeletionTimestamps() - }.subscribeOn(Schedulers.io()) + } } fun peekCallLinks(): Completable { diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogSelectionState.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogSelectionState.kt index 847e363527..70043fcbf3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogSelectionState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogSelectionState.kt @@ -3,17 +3,17 @@ package org.thoughtcrime.securesms.calls.log /** * Selection state object for call logs. */ -sealed class CallLogSelectionState { - abstract fun contains(callId: CallLogRow.Id): Boolean - abstract fun isNotEmpty(totalCount: Int): Boolean +sealed interface CallLogSelectionState { + fun contains(callId: CallLogRow.Id): Boolean + fun isNotEmpty(totalCount: Int): Boolean - abstract fun count(totalCount: Int): Int + fun count(totalCount: Int): Int - abstract fun selected(): Set + fun selected(): Set fun isExclusionary(): Boolean = this is Excludes - protected abstract fun select(callId: CallLogRow.Id): CallLogSelectionState - protected abstract fun deselect(callId: CallLogRow.Id): CallLogSelectionState + fun select(callId: CallLogRow.Id): CallLogSelectionState + fun deselect(callId: CallLogRow.Id): CallLogSelectionState fun toggle(callId: CallLogRow.Id): CallLogSelectionState { return if (contains(callId)) { @@ -26,7 +26,7 @@ sealed class CallLogSelectionState { /** * Includes contains an opt-in list of call logs. */ - data class Includes(private val includes: Set) : CallLogSelectionState() { + data class Includes(private val includes: Set) : CallLogSelectionState { override fun contains(callId: CallLogRow.Id): Boolean { return includes.contains(callId) } @@ -55,7 +55,7 @@ sealed class CallLogSelectionState { /** * Excludes contains an opt-out list of call logs. */ - data class Excludes(private val excluded: Set) : CallLogSelectionState() { + data class Excludes(private val excluded: Set) : CallLogSelectionState { override fun contains(callId: CallLogRow.Id): Boolean = !excluded.contains(callId) override fun isNotEmpty(totalCount: Int): Boolean = excluded.size < totalCount @@ -74,8 +74,10 @@ sealed class CallLogSelectionState { override fun selected(): Set = excluded } + object All : CallLogSelectionState by Excludes(emptySet()) + companion object { fun empty(): CallLogSelectionState = Includes(emptySet()) - fun selectAll(): CallLogSelectionState = Excludes(emptySet()) + fun selectAll(): CallLogSelectionState = All } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogStagedDeletion.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogStagedDeletion.kt index d6523e67a1..f4e3067973 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogStagedDeletion.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogStagedDeletion.kt @@ -35,14 +35,21 @@ class CallLogStagedDeletion( .map { it.roomId } .toSet() - return if (stateSnapshot.isExclusionary()) { - repository.deleteAllCallLogsExcept(callRowIds, filter == CallLogFilter.MISSED).andThen( - repository.deleteAllCallLinksExcept(callRowIds, callLinkIds) - ) - } else { - repository.deleteSelectedCallLogs(callRowIds).andThen( - repository.deleteSelectedCallLinks(callRowIds, callLinkIds) - ) + return when { + stateSnapshot is CallLogSelectionState.All && filter == CallLogFilter.ALL -> { + repository.deleteAllCallLogsOnOrBeforeNow() + } + stateSnapshot is CallLogSelectionState.Excludes || stateSnapshot is CallLogSelectionState.All -> { + repository.deleteAllCallLogsExcept(callRowIds, filter == CallLogFilter.MISSED).andThen( + repository.deleteAllCallLinksExcept(callRowIds, callLinkIds) + ) + } + stateSnapshot is CallLogSelectionState.Includes -> { + repository.deleteSelectedCallLogs(callRowIds).andThen( + repository.deleteSelectedCallLinks(callRowIds, callLinkIds) + ) + } + else -> error("Unhandled state $stateSnapshot $filter") } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/CallLinkUpdateSendJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/CallLinkUpdateSendJob.kt new file mode 100644 index 0000000000..f2b8941753 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/CallLinkUpdateSendJob.kt @@ -0,0 +1,95 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.jobs + +import com.google.protobuf.ByteString +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.jobs.protos.CallLinkUpdateSendJobData +import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId +import org.thoughtcrime.securesms.util.FeatureFlags +import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage +import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException +import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException +import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.CallLinkUpdate +import java.lang.Exception +import java.util.Optional +import java.util.concurrent.TimeUnit + +/** + * Sends a [CallLinkUpdate] message to linked devices. + */ +class CallLinkUpdateSendJob private constructor( + parameters: Parameters, + private val callLinkRoomId: CallLinkRoomId +) : BaseJob(parameters) { + + companion object { + const val KEY = "CallLinkUpdateSendJob" + private val TAG = Log.tag(CallLinkUpdateSendJob::class.java) + } + + constructor( + callLinkRoomId: CallLinkRoomId + ) : this( + Parameters.Builder() + .setQueue("CallLinkUpdateSendJob") + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(Parameters.UNLIMITED) + .addConstraint(NetworkConstraint.KEY) + .build(), + callLinkRoomId + ) + + override fun serialize(): ByteArray = CallLinkUpdateSendJobData.Builder() + .callLinkRoomId(callLinkRoomId.serialize()) + .build() + .encode() + + override fun getFactoryKey(): String = KEY + + override fun onFailure() = Unit + + override fun onRun() { + if (!FeatureFlags.adHocCalling()) { + Log.i(TAG, "Call links are not enabled. Exiting.") + return + } + + val callLink = SignalDatabase.callLinks.getCallLinkByRoomId(callLinkRoomId) + if (callLink?.credentials == null) { + Log.i(TAG, "Call link not found or missing credentials. Exiting.") + return + } + + val callLinkUpdate = CallLinkUpdate.newBuilder() + .setRootKey(ByteString.copyFrom(callLink.credentials.linkKeyBytes)) + .build() + + ApplicationDependencies.getSignalServiceMessageSender() + .sendSyncMessage(SignalServiceSyncMessage.forCallLinkUpdate(callLinkUpdate), Optional.empty()) + } + + override fun onShouldRetry(e: Exception): Boolean { + return when (e) { + is ServerRejectedException -> false + is PushNetworkException -> true + else -> false + } + } + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): CallLinkUpdateSendJob { + return CallLinkUpdateSendJob( + parameters, + CallLinkRoomId.DatabaseSerializer.deserialize(CallLinkUpdateSendJobData.ADAPTER.decode(serializedData!!).callLinkRoomId) + ) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/CallLogEventSendJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/CallLogEventSendJob.kt new file mode 100644 index 0000000000..70d1100529 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/CallLogEventSendJob.kt @@ -0,0 +1,83 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.jobs + +import okio.ByteString +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.jobs.protos.CallLogEventSendJobData +import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage +import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException +import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException +import org.whispersystems.signalservice.internal.push.SignalServiceProtos +import java.util.Optional +import java.util.concurrent.TimeUnit + +/** + * Sends CallLogEvents to synced devices. + */ +class CallLogEventSendJob private constructor( + parameters: Parameters, + private val callLogEvent: SignalServiceProtos.SyncMessage.CallLogEvent +) : BaseJob(parameters) { + + companion object { + const val KEY = "CallLogEventSendJob" + + fun forClearHistory( + timestamp: Long + ) = CallLogEventSendJob( + Parameters.Builder() + .setQueue("CallLogEventSendJob") + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(Parameters.UNLIMITED) + .addConstraint(NetworkConstraint.KEY) + .build(), + SignalServiceProtos.SyncMessage.CallLogEvent + .newBuilder() + .setTimestamp(timestamp) + .setType(SignalServiceProtos.SyncMessage.CallLogEvent.Type.CLEAR) + .build() + ) + } + + override fun serialize(): ByteArray = CallLogEventSendJobData.Builder() + .callLogEvent(ByteString.of(*callLogEvent.toByteArray())) + .build() + .encode() + + override fun getFactoryKey(): String = KEY + + override fun onFailure() = Unit + + override fun onRun() { + ApplicationDependencies.getSignalServiceMessageSender() + .sendSyncMessage( + SignalServiceSyncMessage.forCallLogEvent(callLogEvent), + Optional.empty() + ) + } + + override fun onShouldRetry(e: Exception): Boolean { + return when (e) { + is ServerRejectedException -> false + is PushNetworkException -> true + else -> false + } + } + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): CallLogEventSendJob { + return CallLogEventSendJob( + parameters, + SignalServiceProtos.SyncMessage.CallLogEvent.parseFrom( + CallLogEventSendJobData.ADAPTER.decode(serializedData!!).callLogEvent.toByteArray() + ) + ) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java index 6a92543ee9..cd647bf494 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -102,6 +102,8 @@ public final class JobManagerFactories { put(AvatarGroupsV2DownloadJob.KEY, new AvatarGroupsV2DownloadJob.Factory()); put(BoostReceiptRequestResponseJob.KEY, new BoostReceiptRequestResponseJob.Factory()); put(CallLinkPeekJob.KEY, new CallLinkPeekJob.Factory()); + put(CallLinkUpdateSendJob.KEY, new CallLinkUpdateSendJob.Factory()); + put(CallLogEventSendJob.KEY, new CallLogEventSendJob.Factory()); put(CallSyncEventJob.KEY, new CallSyncEventJob.Factory()); put(CheckServiceReachabilityJob.KEY, new CheckServiceReachabilityJob.Factory()); put(CleanPreKeysJob.KEY, new CleanPreKeysJob.Factory()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/links/CallLinkRoomId.kt b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/links/CallLinkRoomId.kt index f6194730f5..c93ae7f4e8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/links/CallLinkRoomId.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/links/CallLinkRoomId.kt @@ -17,6 +17,7 @@ class CallLinkRoomId private constructor(private val roomId: ByteArray) : Parcel fun serialize(): String = DatabaseSerializer.serialize(this) fun encodeForProto(): ByteString = ByteString.copyFrom(roomId) + override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false diff --git a/app/src/main/protowire/JobData.proto b/app/src/main/protowire/JobData.proto index d47017424b..4d45f80dff 100644 --- a/app/src/main/protowire/JobData.proto +++ b/app/src/main/protowire/JobData.proto @@ -19,4 +19,12 @@ message CallSyncEventJobData { message CallLinkRefreshSinceTimestampJobData { uint64 timestamp = 1; +} + +message CallLogEventSendJobData { + bytes callLogEvent = 1; +} + +message CallLinkUpdateSendJobData { + string callLinkRoomId = 1; } \ No newline at end of file diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java index b189f948e1..8cf2b2d100 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java @@ -743,6 +743,10 @@ public class SignalServiceMessageSender { urgent = message.getRequest().get().isUrgent(); } else if (message.getCallEvent().isPresent()) { content = createCallEventContent(message.getCallEvent().get()); + } else if (message.getCallLinkUpdate().isPresent()) { + content = createCallLinkUpdateContent(message.getCallLinkUpdate().get()); + } else if (message.getCallLogEvent().isPresent()) { + content = createCallLogEventContent(message.getCallLogEvent().get()); } else { throw new IOException("Unsupported sync message!"); } @@ -1701,6 +1705,20 @@ public class SignalServiceMessageSender { return container.setSyncMessage(builder).build(); } + private Content createCallLinkUpdateContent(SyncMessage.CallLinkUpdate proto) { + Content.Builder container = Content.newBuilder(); + SyncMessage.Builder builder = createSyncMessageBuilder().setCallLinkUpdate(proto); + + return container.setSyncMessage(builder).build(); + } + + private Content createCallLogEventContent(SyncMessage.CallLogEvent proto) { + Content.Builder container = Content.newBuilder(); + SyncMessage.Builder builder = createSyncMessageBuilder().setCallLogEvent(proto); + + return container.setSyncMessage(builder).build(); + } + private SyncMessage.Builder createSyncMessageBuilder() { SecureRandom random = new SecureRandom(); byte[] padding = Util.getRandomLengthBytes(512); diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/SignalServiceSyncMessage.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/SignalServiceSyncMessage.java index 20aa1c932c..2171b156d4 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/SignalServiceSyncMessage.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/SignalServiceSyncMessage.java @@ -7,10 +7,10 @@ package org.whispersystems.signalservice.api.messages.multidevice; -import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.internal.push.SignalServiceProtos; import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.CallEvent; import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.CallLinkUpdate; +import org.whispersystems.signalservice.internal.push.SignalServiceProtos.SyncMessage.CallLogEvent; import java.util.LinkedList; import java.util.List; @@ -36,6 +36,7 @@ public class SignalServiceSyncMessage { private final Optional> views; private final Optional callEvent; private final Optional callLinkUpdate; + private final Optional callLogEvent; private SignalServiceSyncMessage(Optional sent, Optional contacts, @@ -52,7 +53,8 @@ public class SignalServiceSyncMessage { Optional outgoingPaymentMessage, Optional> views, Optional callEvent, - Optional callLinkUpdate) + Optional callLinkUpdate, + Optional callLogEvent) { this.sent = sent; this.contacts = contacts; @@ -70,6 +72,7 @@ public class SignalServiceSyncMessage { this.views = views; this.callEvent = callEvent; this.callLinkUpdate = callLinkUpdate; + this.callLogEvent = callLogEvent; } public static SignalServiceSyncMessage forSentTranscript(SentTranscriptMessage sent) { @@ -88,6 +91,7 @@ public class SignalServiceSyncMessage { Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -107,6 +111,7 @@ public class SignalServiceSyncMessage { Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -126,6 +131,7 @@ public class SignalServiceSyncMessage { Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -145,6 +151,7 @@ public class SignalServiceSyncMessage { Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -164,6 +171,7 @@ public class SignalServiceSyncMessage { Optional.empty(), Optional.of(views), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -183,6 +191,7 @@ public class SignalServiceSyncMessage { Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -205,6 +214,7 @@ public class SignalServiceSyncMessage { Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -224,6 +234,7 @@ public class SignalServiceSyncMessage { Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -243,6 +254,7 @@ public class SignalServiceSyncMessage { Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -262,6 +274,7 @@ public class SignalServiceSyncMessage { Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -281,6 +294,7 @@ public class SignalServiceSyncMessage { Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -300,6 +314,7 @@ public class SignalServiceSyncMessage { Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -319,6 +334,7 @@ public class SignalServiceSyncMessage { Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -338,6 +354,7 @@ public class SignalServiceSyncMessage { Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -357,6 +374,7 @@ public class SignalServiceSyncMessage { Optional.of(outgoingPaymentMessage), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -376,6 +394,7 @@ public class SignalServiceSyncMessage { Optional.empty(), Optional.empty(), Optional.of(callEvent), + Optional.empty(), Optional.empty()); } @@ -395,7 +414,28 @@ public class SignalServiceSyncMessage { Optional.empty(), Optional.empty(), Optional.empty(), - Optional.of(callLinkUpdate)); + Optional.of(callLinkUpdate), + Optional.empty()); + } + + public static SignalServiceSyncMessage forCallLogEvent(@Nonnull CallLogEvent callLogEvent) { + return new SignalServiceSyncMessage(Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.of(callLogEvent)); } public static SignalServiceSyncMessage empty() { @@ -414,6 +454,7 @@ public class SignalServiceSyncMessage { Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty()); } @@ -477,6 +518,14 @@ public class SignalServiceSyncMessage { return callEvent; } + public Optional getCallLinkUpdate() { + return callLinkUpdate; + } + + public Optional getCallLogEvent() { + return callLogEvent; + } + public enum FetchType { LOCAL_PROFILE, STORAGE_MANIFEST,