diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActionBarView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActionBarView.kt index 62963b738..59ac8bb2b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActionBarView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActionBarView.kt @@ -16,11 +16,11 @@ import dagger.hilt.android.AndroidEntryPoint import network.loki.messenger.R import network.loki.messenger.databinding.ViewConversationActionBarBinding import network.loki.messenger.databinding.ViewConversationSettingBinding +import network.loki.messenger.libsession_util.util.ExpiryMode import org.session.libsession.messaging.messages.ExpirationConfiguration import org.session.libsession.messaging.open_groups.OpenGroup import org.session.libsession.utilities.ExpirationUtil import org.session.libsession.utilities.recipients.Recipient -import org.session.libsignal.protos.SignalServiceProtos import org.thoughtcrime.securesms.conversation.v2.utilities.MentionManagerUtilities import org.thoughtcrime.securesms.database.GroupDatabase import org.thoughtcrime.securesms.database.LokiAPIDatabase @@ -111,14 +111,14 @@ class ConversationActionBarView : LinearLayout { fun updateSubtitle(recipient: Recipient, openGroup: OpenGroup? = null, config: ExpirationConfiguration? = null) { val settings = mutableListOf() if (config?.isEnabled == true) { - val prefix = if (config.expirationType == SignalServiceProtos.Content.ExpirationType.DELETE_AFTER_READ) { + val prefix = if (config.expiryMode is ExpiryMode.AfterRead) { context.getString(R.string.expiration_type_disappear_after_read) } else { context.getString(R.string.expiration_type_disappear_after_send) } settings.add( ConversationSetting( - "$prefix - ${ExpirationUtil.getExpirationAbbreviatedDisplayValue(context, config.durationSeconds)}" , + "$prefix - ${ExpirationUtil.getExpirationAbbreviatedDisplayValue(context, config.expiryMode?.expirySeconds!!)}" , ConversationSettingType.EXPIRATION, R.drawable.ic_timer ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/expiration/ExpirationSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/expiration/ExpirationSettingsViewModel.kt index a891abecc..f169e2c70 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/expiration/ExpirationSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/expiration/ExpirationSettingsViewModel.kt @@ -12,18 +12,22 @@ import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import network.loki.messenger.libsession_util.util.ExpiryMode import org.session.libsession.messaging.messages.ExpirationConfiguration import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate import org.session.libsession.messaging.sending_receiving.MessageSender import org.session.libsession.snode.SnodeAPI import org.session.libsession.utilities.SSKEnvironment.MessageExpirationManagerProtocol import org.session.libsession.utilities.TextSecurePreferences +import org.session.libsession.utilities.expiryType import org.session.libsession.utilities.recipients.Recipient +import org.session.libsession.utilities.typeRadioIndex import org.session.libsignal.protos.SignalServiceProtos.Content.ExpirationType import org.thoughtcrime.securesms.database.GroupDatabase import org.thoughtcrime.securesms.database.Storage import org.thoughtcrime.securesms.database.ThreadDatabase import org.thoughtcrime.securesms.preferences.RadioOption +import kotlin.reflect.KClass class ExpirationSettingsViewModel( private val threadId: Long, @@ -56,6 +60,7 @@ class ExpirationSettingsViewModel( init { viewModelScope.launch { expirationConfig = storage.getExpirationConfiguration(threadId) + val expirationType = expirationConfig?.expiryMode val recipient = threadDb.getRecipientForThreadId(threadId) _recipient.value = recipient val groupInfo = if (recipient?.isClosedGroupRecipient == true) { @@ -71,14 +76,14 @@ class ExpirationSettingsViewModel( if (recipient?.isLocalNumber == true || recipient?.isClosedGroupRecipient == true) { ExpirationType.DELETE_AFTER_SEND.number } else { - expirationConfig?.typeRadioIndex() ?: -1 + expirationType?.typeRadioIndex() ?: -1 } } else { - expirationConfig?.expirationTypeValue?.let { 0 /* Legacy */ } ?: -1 + if (expirationType != null) 0 else -1 } - _selectedExpirationTimer.value = when(expirationConfig?.expirationType) { - null, ExpirationType.DELETE_AFTER_SEND -> afterSendOptions.find { it.value.toIntOrNull() == expirationConfig?.durationSeconds } - ExpirationType.DELETE_AFTER_READ -> afterReadOptions.find { it.value.toIntOrNull() == expirationConfig?.durationSeconds } + _selectedExpirationTimer.value = when(expirationType) { + is ExpiryMode.AfterSend -> afterSendOptions.find { it.value.toIntOrNull() == expirationType.expirySeconds.toInt() } + is ExpiryMode.AfterRead -> afterReadOptions.find { it.value.toIntOrNull() == expirationType.expirySeconds.toInt() } else -> afterSendOptions.firstOrNull() } } @@ -106,15 +111,22 @@ class ExpirationSettingsViewModel( _selectedExpirationTimer.value = option } + private fun KClass?.withTime(expirationTimer: Long) = when(this) { + ExpiryMode.AfterRead::class -> ExpiryMode.AfterRead(expirationTimer) + ExpiryMode.AfterSend::class -> ExpiryMode.AfterSend(expirationTimer) + else -> ExpiryMode.NONE + } + fun onSetClick() = viewModelScope.launch { var typeValue = _selectedExpirationType.value if (typeValue == 0) { typeValue = ExpirationType.DELETE_AFTER_READ_VALUE } - val expiryType = typeValue.expiryType() val expirationTimer = _selectedExpirationTimer.value?.value?.toIntOrNull() ?: 0 + val expiryTypeClass = typeValue.expiryType() + val expiryMode = expiryTypeClass?.withTime(expirationTimer.toLong()) val address = recipient.value?.address - if (address == null || (expirationConfig?.typeRadioIndex() == typeValue && expirationConfig?.durationSeconds == expirationTimer)) { + if (address == null || (expirationConfig?.expiryMode?.javaClass == expiryTypeClass && expirationConfig?.expiryMode?.expirySeconds?.toInt() == expirationTimer)) { _uiState.update { it.copy(settingsSaved = false) } @@ -122,13 +134,13 @@ class ExpirationSettingsViewModel( } val expiryChangeTimestampMs = SnodeAPI.nowWithOffset - storage.setExpirationConfiguration(ExpirationConfiguration(threadId, expirationTimer, expiryType, expiryChangeTimestampMs)) + storage.setExpirationConfiguration(ExpirationConfiguration(threadId, expiryMode, expiryChangeTimestampMs)) val message = ExpirationTimerUpdate(expirationTimer) message.sender = textSecurePreferences.getLocalNumber() message.recipient = address.serialize() message.sentTimestamp = expiryChangeTimestampMs - messageExpirationManager.setExpirationTimer(message, expiryType) + messageExpirationManager.setExpirationTimer(message, expiryMode) MessageSender.send(message, address) _uiState.update { @@ -177,18 +189,3 @@ data class ExpirationSettingsUiState( val showExpirationTypeSelector: Boolean = false, val settingsSaved: Boolean? = null ) - -fun ExpirationConfiguration?.typeRadioIndex(): Int { - if (this == null || expirationType == null) return -1 - return when { - expirationType == ExpirationType.DELETE_AFTER_READ -> ExpirationType.DELETE_AFTER_READ_VALUE - else -> -1 - } - - return if (expirationType == ) -} - -fun Int.expiryType(): ExpirationType? { - if (this == -1) return null - TODO() -} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 85a8fccdf..9b241a536 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -62,6 +62,7 @@ import kotlinx.coroutines.withContext import network.loki.messenger.R import network.loki.messenger.databinding.ActivityConversationV2Binding import network.loki.messenger.databinding.ViewVisibleMessageBinding +import network.loki.messenger.libsession_util.util.ExpiryMode import nl.komponents.kovenant.ui.successUi import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.contacts.Contact @@ -94,7 +95,6 @@ import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient.DisappearingState import org.session.libsession.utilities.recipients.RecipientModifiedListener import org.session.libsignal.crypto.MnemonicCodec -import org.session.libsignal.protos.SignalServiceProtos.Content.ExpirationType import org.session.libsignal.utilities.IdPrefix import org.session.libsignal.utilities.ListenableFuture import org.session.libsignal.utilities.Log @@ -1574,8 +1574,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe val message = VisibleMessage() message.sentTimestamp = sentTimestamp message.text = text - val expiresInMillis = (viewModel.expirationConfiguration?.durationSeconds ?: 0) * 1000L - val expireStartedAt = if (viewModel.expirationConfiguration?.expirationType == ExpirationType.DELETE_AFTER_SEND) { + val expiresInMillis = (viewModel.expirationConfiguration?.expiryMode?.expirySeconds ?: 0) * 1000L + val expireStartedAt = if (viewModel.expirationConfiguration?.expiryMode is ExpiryMode.AfterSend) { message.sentTimestamp!! } else 0 val outgoingTextMessage = OutgoingTextMessage.from(message, recipient, expiresInMillis, expireStartedAt) @@ -1617,8 +1617,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe else it.individualRecipient.address quote?.copy(author = sender) } - val expiresInMs = (viewModel.expirationConfiguration?.durationSeconds ?: 0) * 1000L - val expireStartedAtMs = if (viewModel.expirationConfiguration?.expirationType == ExpirationType.DELETE_AFTER_SEND) { + val expiresInMs = (viewModel.expirationConfiguration?.expiryMode?.expirySeconds ?: 0) * 1000L + val expireStartedAtMs = if (viewModel.expirationConfiguration?.expiryMode is ExpiryMode.AfterSend) { sentTimestamp } else 0 val outgoingTextMessage = OutgoingMediaMessage.from(message, recipient, attachments, localQuote, linkPreview, expiresInMs, expireStartedAtMs) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt index ad78cc96d..aa7254c90 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -72,7 +72,6 @@ import org.session.libsignal.crypto.ecc.DjbECPublicKey import org.session.libsignal.crypto.ecc.ECKeyPair import org.session.libsignal.messages.SignalServiceAttachmentPointer import org.session.libsignal.messages.SignalServiceGroup -import org.session.libsignal.protos.SignalServiceProtos.Content.ExpirationType import org.session.libsignal.utilities.Base64 import org.session.libsignal.utilities.Hex import org.session.libsignal.utilities.IdPrefix @@ -320,8 +319,9 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co message.threadID = getOrCreateThreadIdFor(targetAddress) } val expirationConfig = getExpirationConfiguration(message.threadID ?: -1) - val expiresInMillis = (expirationConfig?.durationSeconds ?: 0) * 1000L - val expireStartedAt = if (expirationConfig?.expirationType == ExpirationType.DELETE_AFTER_SEND) message.sentTimestamp!! else 0 + val expiryMode = expirationConfig?.expiryMode + val expiresInMillis = (expiryMode?.expirySeconds ?: 0) * 1000L + val expireStartedAt = if (expiryMode is ExpiryMode.AfterSend) message.sentTimestamp!! else 0 if (message.isMediaMessage() || attachments.isNotEmpty()) { val quote: Optional = if (quotes != null) Optional.of(quotes) else Optional.absent() val linkPreviews: Optional> = if (linkPreview.isEmpty()) Optional.absent() else Optional.of(linkPreview.mapNotNull { it!! }) @@ -371,7 +371,7 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co DatabaseComponent.get(context).lokiMessageDatabase().setMessageServerHash(id, serverHash) } } - if (expirationConfig?.expirationType == ExpirationType.DELETE_AFTER_SEND) { + if (expiryMode is ExpiryMode.AfterSend) { SSKEnvironment.shared.messageExpirationManager.startAnyExpiration(message.sentTimestamp!!, message.sender!!, expireStartedAt) } return messageID @@ -878,8 +878,6 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co override fun updateGroupConfig(groupPublicKey: String) { val groupID = GroupUtil.doubleEncodeGroupID(groupPublicKey) val groupAddress = fromSerialized(groupID) - // TODO: probably add a check in here for isActive? - // TODO: also check if local user is a member / maybe run delete otherwise? val existingGroup = getGroup(groupID) ?: return Log.w("Loki-DBG", "No existing group for ${groupPublicKey.take(4)}} when updating group config") val userGroups = configFactory.userGroups ?: return @@ -936,14 +934,15 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co val recipient = Recipient.from(context, fromSerialized(groupID), false) val threadId = DatabaseComponent.get(context).threadDatabase().getOrCreateThreadIdFor(recipient) val expirationConfig = getExpirationConfiguration(threadId) - val expiresInMillis = (expirationConfig?.durationSeconds ?: 0) * 100L - val expireStartedAt = if (expirationConfig?.expirationType == ExpirationType.DELETE_AFTER_SEND) sentTimestamp else 0 + val expiryMode = expirationConfig?.expiryMode + val expiresInMillis = (expiryMode?.expirySeconds ?: 0) * 1000L + val expireStartedAt = if (expiryMode is ExpiryMode.AfterSend) sentTimestamp else 0 val m = IncomingTextMessage(fromSerialized(senderPublicKey), 1, sentTimestamp, "", Optional.of(group), expiresInMillis, expireStartedAt, true, false) val updateData = UpdateMessageData.buildGroupUpdate(type, name, members)?.toJSON() val infoMessage = IncomingGroupMessage(m, groupID, updateData, true) val smsDB = DatabaseComponent.get(context).smsDatabase() smsDB.insertMessageInbox(infoMessage, true) - if (expirationConfig?.expirationType == ExpirationType.DELETE_AFTER_SEND) { + if (expiryMode is ExpiryMode.AfterSend) { SSKEnvironment.shared.messageExpirationManager.startAnyExpiration(sentTimestamp, senderPublicKey, expireStartedAt) } } @@ -953,8 +952,9 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co val recipient = Recipient.from(context, fromSerialized(groupID), false) val threadId = DatabaseComponent.get(context).threadDatabase().getOrCreateThreadIdFor(recipient) val expirationConfig = getExpirationConfiguration(threadId) - val expiresInMillis = (expirationConfig?.durationSeconds ?: 0) * 100L - val expireStartedAt = if (expirationConfig?.expirationType == ExpirationType.DELETE_AFTER_SEND) sentTimestamp else 0 + val expiryMode = expirationConfig?.expiryMode + val expiresInMillis = (expiryMode?.expirySeconds ?: 0) * 1000L + val expireStartedAt = if (expiryMode is ExpiryMode.AfterSend) sentTimestamp else 0 val updateData = UpdateMessageData.buildGroupUpdate(type, name, members)?.toJSON() ?: "" val infoMessage = OutgoingGroupMediaMessage(recipient, updateData, groupID, null, sentTimestamp, expiresInMillis, expireStartedAt, true, null, listOf(), listOf()) val mmsDB = DatabaseComponent.get(context).mmsDatabase() @@ -962,7 +962,7 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co if (mmsSmsDB.getMessageFor(sentTimestamp, userPublicKey) != null) return val infoMessageID = mmsDB.insertMessageOutbox(infoMessage, threadID, false, null, runThreadUpdate = true) mmsDB.markAsSent(infoMessageID, true) - if (expirationConfig?.expirationType == ExpirationType.DELETE_AFTER_SEND) { + if (expiryMode is ExpiryMode.AfterSend) { SSKEnvironment.shared.messageExpirationManager.startAnyExpiration(sentTimestamp, userPublicKey!!, expireStartedAt) } } @@ -1021,7 +1021,7 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co val recipient = Recipient.from(context, fromSerialized(address), false) val threadId = DatabaseComponent.get(context).threadDatabase().getOrCreateThreadIdFor(recipient) DatabaseComponent.get(context).expirationConfigurationDatabase().setExpirationConfiguration( - ExpirationConfiguration(threadId, duration, ExpirationType.DELETE_AFTER_SEND.number, System.currentTimeMillis()) + ExpirationConfiguration(threadId, ExpiryMode.AfterSend(duration.toLong()), SnodeAPI.nowWithOffset) ) if (recipient.isContactRecipient && !recipient.isLocalNumber) { configFactory.contacts?.upsertContact(address) { @@ -1364,8 +1364,9 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co if (recipient.isBlocked) return val threadId = getThreadId(recipient) ?: return val expirationConfig = getExpirationConfiguration(threadId) - val expiresInMillis = (expirationConfig?.durationSeconds ?: 0) * 100L - val expireStartedAt = if (expirationConfig?.expirationType == ExpirationType.DELETE_AFTER_SEND) sentTimestamp else 0 + val expiryMode = expirationConfig?.expiryMode + val expiresInMillis = (expiryMode?.expirySeconds ?: 0) * 1000L + val expireStartedAt = if (expiryMode is ExpiryMode.AfterSend) sentTimestamp else 0 val mediaMessage = IncomingMediaMessage( address, sentTimestamp, @@ -1386,7 +1387,7 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co ) database.insertSecureDecryptedMessageInbox(mediaMessage, threadId, runThreadUpdate = true) - if (expirationConfig?.expirationType == ExpirationType.DELETE_AFTER_SEND) { + if (expiryMode is ExpiryMode.AfterSend) { SSKEnvironment.shared.messageExpirationManager.startAnyExpiration(sentTimestamp, senderPublicKey, expireStartedAt) } } @@ -1470,8 +1471,9 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co recipientDb.setApproved(sender, true) recipientDb.setApprovedMe(sender, true) val expirationConfig = getExpirationConfiguration(threadId) - val expiresInMillis = (expirationConfig?.durationSeconds ?: 0) * 100L - val expireStartedAt = if (expirationConfig?.expirationType == ExpirationType.DELETE_AFTER_SEND) { + val expiryMode = expirationConfig?.expiryMode + val expiresInMillis = (expiryMode?.expirySeconds ?: 0) * 1000L + val expireStartedAt = if (expiryMode is ExpiryMode.AfterSend) { response.sentTimestamp!! } else 0 val message = IncomingMediaMessage( @@ -1522,11 +1524,12 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co val recipient = Recipient.from(context, address, false) val threadId = DatabaseComponent.get(context).threadDatabase().getOrCreateThreadIdFor(recipient) val expirationConfig = getExpirationConfiguration(threadId) - val expiresInMillis = (expirationConfig?.durationSeconds ?: 0) * 100L - val expireStartedAt = if (expirationConfig?.expirationType == ExpirationType.DELETE_AFTER_SEND) sentTimestamp else 0 + val expiryMode = expirationConfig?.expiryMode + val expiresInMillis = (expiryMode?.expirySeconds ?: 0) * 1000L + val expireStartedAt = if (expiryMode is ExpiryMode.AfterSend) sentTimestamp else 0 val callMessage = IncomingTextMessage.fromCallInfo(callMessageType, address, Optional.absent(), sentTimestamp, expiresInMillis, expireStartedAt) database.insertCallMessage(callMessage) - if (expirationConfig?.expirationType == ExpirationType.DELETE_AFTER_SEND) { + if (expiryMode is ExpiryMode.AfterSend) { SSKEnvironment.shared.messageExpirationManager.startAnyExpiration(sentTimestamp, senderPublicKey, expireStartedAt) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java index bc7db0d41..9b24dc6fe 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/AndroidAutoReplyReceiver.java @@ -34,7 +34,6 @@ import org.session.libsession.messaging.sending_receiving.MessageSender; import org.session.libsession.snode.SnodeAPI; import org.session.libsession.utilities.Address; import org.session.libsession.utilities.recipients.Recipient; -import org.session.libsignal.protos.SignalServiceProtos; import org.session.libsignal.utilities.Log; import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; @@ -44,6 +43,8 @@ import org.thoughtcrime.securesms.mms.MmsException; import java.util.Collections; import java.util.List; +import network.loki.messenger.libsession_util.util.ExpiryMode; + /** * Get the response text from the Android Auto and sends an message as a reply */ @@ -87,9 +88,10 @@ public class AndroidAutoReplyReceiver extends BroadcastReceiver { message.setText(responseText.toString()); message.setSentTimestamp(SnodeAPI.getNowWithOffset()); MessageSender.send(message, recipient.getAddress()); - ExpirationConfiguration config = DatabaseComponent.get(context).expirationConfigurationDatabase().getExpirationConfiguration(threadId); - long expiresInMillis = config == null ? 0 : config.getDurationSeconds() * 1000L; - long expireStartedAt = config.getExpirationType() == SignalServiceProtos.Content.ExpirationType.DELETE_AFTER_SEND ? message.getSentTimestamp() : 0L; + ExpirationConfiguration config = DatabaseComponent.get(context).storage().getExpirationConfiguration(threadId); + ExpiryMode expiryMode = config == null ? null : config.getExpiryMode(); + long expiresInMillis = expiryMode == null ? 0 : expiryMode.getExpirySeconds() * 1000L; + long expireStartedAt = expiryMode instanceof ExpiryMode.AfterSend ? message.getSentTimestamp() : 0L; if (recipient.isGroupRecipient()) { Log.w("AndroidAutoReplyReceiver", "GroupRecipient, Sending media message"); diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java index 2875631f7..20a72afe0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java @@ -34,7 +34,6 @@ import org.session.libsession.messaging.sending_receiving.MessageSender; import org.session.libsession.snode.SnodeAPI; import org.session.libsession.utilities.Address; import org.session.libsession.utilities.recipients.Recipient; -import org.session.libsignal.protos.SignalServiceProtos.Content.ExpirationType; import org.session.libsignal.utilities.Log; import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; @@ -50,6 +49,7 @@ import java.util.List; import javax.inject.Inject; import dagger.hilt.android.AndroidEntryPoint; +import network.loki.messenger.libsession_util.util.ExpiryMode; /** * Get the response text from the Wearable Device and sends an message as a reply @@ -97,9 +97,10 @@ public class RemoteReplyReceiver extends BroadcastReceiver { message.setSentTimestamp(SnodeAPI.getNowWithOffset()); message.setText(responseText.toString()); ExpirationConfiguration config = storage.getExpirationConfiguration(threadId); + ExpiryMode expiryMode = config == null ? null : config.getExpiryMode(); - long expiresInMillis = config == null ? 0 : config.getDurationSeconds() * 1000L; - long expireStartedAt = config.getExpirationType() == ExpirationType.DELETE_AFTER_SEND ? message.getSentTimestamp() : 0L; + long expiresInMillis = expiryMode == null ? 0 : expiryMode.getExpirySeconds() * 1000L; + long expireStartedAt = expiryMode instanceof ExpiryMode.AfterSend ? message.getSentTimestamp() : 0L; switch (replyMethod) { case GroupMessage: { OutgoingMediaMessage reply = OutgoingMediaMessage.from(message, recipient, Collections.emptyList(), null, null, expiresInMillis, 0); diff --git a/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt index 7388c2a78..de878a1d4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt @@ -1,5 +1,6 @@ package org.thoughtcrime.securesms.repository +import network.loki.messenger.libsession_util.util.ExpiryMode import org.session.libsession.database.MessageDataProvider import org.session.libsession.messaging.messages.Destination import org.session.libsession.messaging.messages.control.MessageRequestResponse @@ -14,7 +15,6 @@ import org.session.libsession.utilities.Address import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.recipients.Recipient -import org.session.libsignal.protos.SignalServiceProtos.Content.ExpirationType import org.session.libsignal.utilities.toHexString import org.thoughtcrime.securesms.database.DraftDatabase import org.thoughtcrime.securesms.database.ExpirationConfigurationDatabase @@ -122,9 +122,9 @@ class DefaultConversationRepository @Inject constructor( openGroupInvitation.name = openGroup.name openGroupInvitation.url = openGroup.joinURL message.openGroupInvitation = openGroupInvitation - val expirationConfig = configDb.getExpirationConfiguration(threadId) - val expiresInMillis = (expirationConfig?.durationSeconds ?: 0) * 1000L - val expireStartedAt = if (expirationConfig?.expirationType == ExpirationType.DELETE_AFTER_SEND) message.sentTimestamp!! else 0 + val expirationConfig = storage.getExpirationConfiguration(threadId) + val expiresInMillis = (expirationConfig?.expiryMode?.expirySeconds ?: 0) * 1000L + val expireStartedAt = if (expirationConfig?.expiryMode is ExpiryMode.AfterSend) message.sentTimestamp!! else 0 val outgoingTextMessage = OutgoingTextMessage.fromOpenGroupInvitation( openGroupInvitation, contact, diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java b/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java index e0357514c..780184551 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.java @@ -15,7 +15,6 @@ import org.session.libsession.utilities.SSKEnvironment; import org.session.libsession.utilities.TextSecurePreferences; import org.session.libsession.utilities.recipients.Recipient; import org.session.libsignal.messages.SignalServiceGroup; -import org.session.libsignal.protos.SignalServiceProtos.Content.ExpirationType; import org.session.libsignal.utilities.Log; import org.session.libsignal.utilities.guava.Optional; import org.thoughtcrime.securesms.database.MmsDatabase; @@ -31,6 +30,8 @@ import java.util.TreeSet; import java.util.concurrent.Executor; import java.util.concurrent.Executors; +import network.loki.messenger.libsession_util.util.ExpiryMode; + public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationManagerProtocol { private static final String TAG = ExpiringMessageManager.class.getSimpleName(); @@ -73,11 +74,12 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM } @Override - public void setExpirationTimer(@NotNull ExpirationTimerUpdate message, int expiryType) { + public void setExpirationTimer(@NotNull ExpirationTimerUpdate message, ExpiryMode expiryMode) { String userPublicKey = TextSecurePreferences.getLocalNumber(context); String senderPublicKey = message.getSender(); - long expireStartedAt = expiryType == ExpirationType.DELETE_AFTER_SEND_VALUE - ? message.getSentTimestamp() : 0; + long sentTimestamp = message.getSentTimestamp() == null ? 0 : message.getSentTimestamp(); + long expireStartedAt = (expiryMode instanceof ExpiryMode.AfterSend) + ? sentTimestamp : 0; // Notify the user if (senderPublicKey == null || userPublicKey.equals(senderPublicKey)) { @@ -86,7 +88,7 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM } else { insertIncomingExpirationTimerMessage(message, expireStartedAt); } - if (expiryType == ExpirationType.DELETE_AFTER_SEND_VALUE && message.getSentTimestamp() != null && senderPublicKey != null) { + if (expiryMode instanceof ExpiryMode.AfterSend && message.getSentTimestamp() != null && senderPublicKey != null) { startAnyExpiration(message.getSentTimestamp(), senderPublicKey, expireStartedAt); } } @@ -171,14 +173,15 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM MessageRecord messageRecord = mmsSmsDatabase.getMessageFor(timestamp, author); if (messageRecord != null) { boolean mms = messageRecord.isMms(); - ExpirationConfiguration config = DatabaseComponent.get(context).expirationConfigurationDatabase().getExpirationConfiguration(messageRecord.getThreadId()); + ExpirationConfiguration config = DatabaseComponent.get(context).storage().getExpirationConfiguration(messageRecord.getThreadId()); if (config == null || !config.isEnabled()) return; + ExpiryMode mode = config.getExpiryMode(); if (mms) { mmsDatabase.markExpireStarted(messageRecord.getId(), expireStartedAt); } else { smsDatabase.markExpireStarted(messageRecord.getId(), expireStartedAt); } - scheduleDeletion(messageRecord.getId(), mms, expireStartedAt, config.getDurationSeconds() * 1000L); + scheduleDeletion(messageRecord.getId(), mms, expireStartedAt, (mode != null ? mode.getExpirySeconds() : 0) * 1000L); } } diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt index 2855d82c2..676923c72 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt @@ -152,7 +152,11 @@ class BatchMessageReceiveJob( try { when (message) { is VisibleMessage -> { - MessageReceiver.updateExpiryIfNeeded(message, proto, openGroupID, newLastSeen) + MessageReceiver.updateExpiryIfNeeded( + message, + proto, + openGroupID + ) val isUserBlindedSender = message.sender == serverPublicKey?.let { SodiumUtilities.blindedKeyPair( diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/ExpirationConfiguration.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/ExpirationConfiguration.kt index e2b9e9d91..a2f5e2ab8 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/ExpirationConfiguration.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/ExpirationConfiguration.kt @@ -4,10 +4,10 @@ import network.loki.messenger.libsession_util.util.ExpiryMode data class ExpirationConfiguration( val threadId: Long = -1, - val expirationType: ExpiryMode? = null, + val expiryMode: ExpiryMode? = null, val updatedTimestampMs: Long = 0 ) { - val isEnabled = expirationType != null && expirationType.expirySeconds > 0 + val isEnabled = expiryMode != null && expiryMode.expirySeconds > 0 companion object { val isNewConfigEnabled = false /* TODO: System.currentTimeMillis() > 1_676_851_200_000 // 13/02/2023 */ diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/Message.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/Message.kt index 5a5bcb824..cb8233b1c 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/Message.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/Message.kt @@ -1,12 +1,14 @@ package org.session.libsession.messaging.messages import com.google.protobuf.ByteString +import network.loki.messenger.libsession_util.util.ExpiryMode import org.session.libsession.database.StorageProtocol import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate import org.session.libsession.messaging.messages.visible.VisibleMessage import org.session.libsession.utilities.GroupUtil import org.session.libsignal.protos.SignalServiceProtos +import org.session.libsignal.protos.SignalServiceProtos.Content.ExpirationType abstract class Message { var id: Long? = null @@ -60,10 +62,16 @@ abstract class Message { expirationTimer = 0 return this } - if (config.isEnabled) { - expirationTimer = config.durationSeconds + if (config.isEnabled && config.expiryMode != null) { + expirationTimer = config.expiryMode.expirySeconds.toInt() lastDisappearingMessageChangeTimestamp = config.updatedTimestampMs - expirationType = config.expirationType + config.expiryMode.let { expiryMode -> + when (expiryMode) { + is ExpiryMode.AfterSend -> expirationType = ExpirationType.DELETE_AFTER_SEND + is ExpiryMode.AfterRead -> expirationType = ExpirationType.DELETE_AFTER_READ + ExpiryMode.NONE -> { /* do nothing */ } + } + } } return this } diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/control/ConfigurationMessage.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ConfigurationMessage.kt index aed9df9bb..cadfd0744 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/control/ConfigurationMessage.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ConfigurationMessage.kt @@ -4,16 +4,15 @@ import com.google.protobuf.ByteString import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.utilities.Address import org.session.libsession.utilities.GroupUtil -import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.ProfileKeyUtil -import org.session.libsession.utilities.recipients.Recipient +import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.crypto.ecc.DjbECPrivateKey import org.session.libsignal.crypto.ecc.DjbECPublicKey import org.session.libsignal.crypto.ecc.ECKeyPair import org.session.libsignal.protos.SignalServiceProtos +import org.session.libsignal.utilities.Hex import org.session.libsignal.utilities.removingIdPrefixIfNeeded import org.session.libsignal.utilities.toHexString -import org.session.libsignal.utilities.Hex class ConfigurationMessage(var closedGroups: List, var openGroups: List, var contacts: List, var displayName: String, var profilePicture: String?, var profileKey: ByteArray) : ControlMessage() { @@ -130,7 +129,14 @@ class ConfigurationMessage(var closedGroups: List, var openGroups: val encryptionKeyPair = storage.getLatestClosedGroupEncryptionKeyPair(groupPublicKey) ?: continue val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(group.encodedId)) val expiryConfig = storage.getExpirationConfiguration(threadID) - val closedGroup = ClosedGroup(groupPublicKey, group.title, encryptionKeyPair, group.members.map { it.serialize() }, group.admins.map { it.serialize() }, expiryConfig?.durationSeconds ?: 0) + val closedGroup = ClosedGroup( + groupPublicKey, + group.title, + encryptionKeyPair, + group.members.map { it.serialize() }, + group.admins.map { it.serialize() }, + expiryConfig?.expiryMode?.expirySeconds?.toInt() ?: 0 + ) closedGroups.add(closedGroup) } if (group.isOpenGroup) { diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt index e70ba0dec..010369078 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt @@ -1,5 +1,6 @@ package org.session.libsession.messaging.sending_receiving +import network.loki.messenger.libsession_util.util.ExpiryMode import nl.komponents.kovenant.Promise import nl.komponents.kovenant.deferred import org.session.libsession.messaging.MessagingModuleConfiguration @@ -33,7 +34,6 @@ import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.SSKEnvironment import org.session.libsignal.crypto.PushTransportDetails import org.session.libsignal.protos.SignalServiceProtos -import org.session.libsignal.protos.SignalServiceProtos.Content.ExpirationType import org.session.libsignal.utilities.Base64 import org.session.libsignal.utilities.IdPrefix import org.session.libsignal.utilities.Namespace @@ -256,8 +256,9 @@ object MessageSender { storage.getOrCreateThreadIdFor(Address.fromSerialized(address!!)) } val config = storage.getExpirationConfiguration(threadId) ?: return null - return if (config.isEnabled && (config.expirationType == ExpirationType.DELETE_AFTER_SEND || isSyncMessage)) { - config.durationSeconds * 1000L + val expiryMode = config.expiryMode + return if (config.isEnabled && (expiryMode is ExpiryMode.AfterSend || isSyncMessage)) { + (expiryMode?.expirySeconds ?: 0L) * 1000L } else null } diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroupHandler.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroupHandler.kt index 4dc908873..41b7a894d 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroupHandler.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroupHandler.kt @@ -131,7 +131,7 @@ fun MessageSender.addMembers(groupPublicKey: String, membersToAdd: List) throw Error.NoThread } val threadId = storage.getOrCreateThreadIdFor(fromSerialized(groupID)) - val expireTimer = storage.getExpirationConfiguration(threadId)?.durationSeconds ?: 0 + val expireTimer = storage.getExpirationConfiguration(threadId)?.expiryMode?.expirySeconds ?: 0 if (membersToAdd.isEmpty()) { Log.d("Loki", "Invalid closed group update.") throw Error.InvalidClosedGroupUpdate @@ -156,7 +156,14 @@ fun MessageSender.addMembers(groupPublicKey: String, membersToAdd: List) send(closedGroupControlMessage, Address.fromSerialized(groupID)) // Send closed group update messages to any new members individually for (member in membersToAdd) { - val closedGroupNewKind = ClosedGroupControlMessage.Kind.New(ByteString.copyFrom(Hex.fromStringCondensed(groupPublicKey)), name, encryptionKeyPair, membersAsData, adminsAsData, expireTimer) + val closedGroupNewKind = ClosedGroupControlMessage.Kind.New( + ByteString.copyFrom(Hex.fromStringCondensed(groupPublicKey)), + name, + encryptionKeyPair, + membersAsData, + adminsAsData, + expireTimer.toInt() + ) val closedGroupControlMessage = ClosedGroupControlMessage(closedGroupNewKind, groupID) // It's important that the sent timestamp of this message is greater than the sent timestamp // of the `MembersAdded` message above. The reason is that upon receiving this `New` message, diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt index ad004a97d..93fad9d2b 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/ReceivedMessageHandler.kt @@ -2,6 +2,7 @@ package org.session.libsession.messaging.sending_receiving import android.text.TextUtils import network.loki.messenger.libsession_util.ConfigBase +import network.loki.messenger.libsession_util.util.ExpiryMode import org.session.libsession.avatars.AvatarHelper import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.jobs.BackgroundGroupAddJob @@ -39,6 +40,7 @@ import org.session.libsession.utilities.GroupUtil.doubleEncodeGroupID import org.session.libsession.utilities.ProfileKeyUtil import org.session.libsession.utilities.SSKEnvironment import org.session.libsession.utilities.TextSecurePreferences +import org.session.libsession.utilities.expiryMode import org.session.libsession.utilities.recipients.Recipient import org.session.libsignal.crypto.ecc.DjbECPrivateKey import org.session.libsignal.crypto.ecc.DjbECPublicKey @@ -67,7 +69,7 @@ fun MessageReceiver.handle(message: Message, proto: SignalServiceProtos.Content, // Do nothing if the message was outdated if (MessageReceiver.messageIsOutdated(message, threadId, openGroupID)) { return } - MessageReceiver.updateExpiryIfNeeded(message, proto, openGroupID) + MessageReceiver.updateExpiryIfNeeded(message, proto, openGroupID, ) when (message) { is ReadReceipt -> handleReadReceipt(message) is TypingIndicator -> handleTypingIndicator(message) @@ -157,8 +159,8 @@ private fun MessageReceiver.handleExpirationTimerUpdate(message: ExpirationTimer val module = MessagingModuleConfiguration.shared val recipient = Recipient.from(module.context, Address.fromSerialized(message.sender!!), false) val type = when { - recipient.isContactRecipient -> ExpirationType.DELETE_AFTER_READ - recipient.isGroupRecipient -> ExpirationType.DELETE_AFTER_SEND + recipient.isContactRecipient -> ExpiryMode.AfterRead(message.duration!!.toLong()) + recipient.isGroupRecipient -> ExpiryMode.AfterSend(message.duration!!.toLong()) else -> null } try { @@ -167,12 +169,16 @@ private fun MessageReceiver.handleExpirationTimerUpdate(message: ExpirationTimer threadId = module.storage.getOrCreateThreadIdFor(fromSerialized(doubleEncodeGroupID(message.groupPublicKey!!))) } module.storage.setExpirationConfiguration( - ExpirationConfiguration(threadId, message.duration!!, type?.number ?: -1, System.currentTimeMillis()) + ExpirationConfiguration( + threadId, + type, + SnodeAPI.nowWithOffset + ) ) } catch (e: Exception) { Log.e("Loki", "Failed to update expiration configuration.") } - SSKEnvironment.shared.messageExpirationManager.setExpirationTimer(message, type?.number ?: -1) + SSKEnvironment.shared.messageExpirationManager.setExpirationTimer(message, type) } private fun MessageReceiver.handleDataExtractionNotification(message: DataExtractionNotification) { @@ -270,23 +276,30 @@ fun handleMessageRequestResponse(message: MessageRequestResponse) { } //endregion -fun MessageReceiver.updateExpiryIfNeeded(message: Message, proto: SignalServiceProtos.Content, openGroupID: String?, lastSeen: Long) { +fun MessageReceiver.updateExpiryIfNeeded( + message: Message, + proto: SignalServiceProtos.Content, + openGroupID: String? +) { val storage = MessagingModuleConfiguration.shared.storage val sentTime = message.sentTimestamp ?: throw MessageReceiver.Error.InvalidMessage if (!proto.hasLastDisappearingMessageChangeTimestamp()) return - val threadID = storage.getThreadIdFor(message.sender!!, message.groupPublicKey, openGroupID, false) - if (threadID == null) throw MessageReceiver.Error.NoThread + val threadID = + storage.getThreadIdFor(message.sender!!, message.groupPublicKey, openGroupID, false) + ?: throw MessageReceiver.Error.NoThread val recipient = storage.getRecipientForThread(threadID) ?: throw MessageReceiver.Error.NoThread val localConfig = storage.getExpirationConfiguration(threadID) val durationSeconds = if (proto.hasExpirationTimer()) proto.expirationTimer else 0 val type = if (proto.hasExpirationType()) proto.expirationType else null + + val expiryMode = type?.expiryMode(durationSeconds.toLong()) + val remoteConfig = ExpirationConfiguration( threadID, - durationSeconds, - type?.number ?: -1, + expiryMode, proto.lastDisappearingMessageChangeTimestamp ) @@ -312,12 +325,12 @@ fun MessageReceiver.updateExpiryIfNeeded(message: Message, proto: SignalServiceP // handle a delete after send expired fetch if (type == ExpirationType.DELETE_AFTER_SEND - && sentTime + configToUse.durationSeconds <= SnodeAPI.nowWithOffset) { + && sentTime + (configToUse.expiryMode?.expirySeconds ?: 0) <= SnodeAPI.nowWithOffset) { throw MessageReceiver.Error.ExpiredMessage } - // handle a delete after read last known config value (test) TODO: actually implement this with shared config library + // handle a delete after read last known config value if (type == ExpirationType.DELETE_AFTER_READ - && sentTime + configToUse.durationSeconds <= ExpirationConfiguration.LAST_READ_TEST) { + && sentTime + (configToUse.expiryMode?.expirySeconds ?: 0) <= storage.getLastSeen(threadID)) { throw MessageReceiver.Error.ExpiredMessage } @@ -326,7 +339,7 @@ fun MessageReceiver.updateExpiryIfNeeded(message: Message, proto: SignalServiceP } if (message is ExpirationTimerUpdate) { - SSKEnvironment.shared.messageExpirationManager.setExpirationTimer(message, type?.number ?: -1) + SSKEnvironment.shared.messageExpirationManager.setExpirationTimer(message, type?.expiryMode(durationSeconds.toLong())) } } diff --git a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt index d78b2cfa3..89dae57f4 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt @@ -1,6 +1,7 @@ package org.session.libsession.messaging.utilities import android.content.Context +import network.loki.messenger.libsession_util.util.ExpiryMode import org.session.libsession.R import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.calls.CallMessageType @@ -14,7 +15,6 @@ import org.session.libsession.messaging.sending_receiving.data_extraction.DataEx import org.session.libsession.utilities.Address import org.session.libsession.utilities.ExpirationUtil import org.session.libsession.utilities.truncateIdForDisplay -import org.session.libsignal.protos.SignalServiceProtos.Content.ExpirationType object UpdateMessageBuilder { val storage = MessagingModuleConfiguration.shared.storage @@ -99,9 +99,9 @@ object UpdateMessageBuilder { val time = ExpirationUtil.getExpirationDisplayValue(context, duration.toInt()) val threadId = storage.getThreadId(Address.fromSerialized(senderId!!)) val config = threadId?.let { storage.getExpirationConfiguration(it) } - val state = when (config?.expirationType) { - ExpirationType.DELETE_AFTER_SEND -> context.getString(R.string.MessageRecord_state_sent) - ExpirationType.DELETE_AFTER_READ -> context.getString(R.string.MessageRecord_state_read) + val state = when (config?.expiryMode) { + is ExpiryMode.AfterSend -> context.getString(R.string.MessageRecord_state_sent) + is ExpiryMode.AfterRead -> context.getString(R.string.MessageRecord_state_read) else -> "" } if (isOutgoing) { diff --git a/libsession/src/main/java/org/session/libsession/utilities/ExpirationUtil.java b/libsession/src/main/java/org/session/libsession/utilities/ExpirationUtil.java index 88130f3f9..634a6ea19 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/ExpirationUtil.java +++ b/libsession/src/main/java/org/session/libsession/utilities/ExpirationUtil.java @@ -2,10 +2,10 @@ package org.session.libsession.utilities; import android.content.Context; -import java.util.concurrent.TimeUnit; - import org.session.libsession.R; +import java.util.concurrent.TimeUnit; + public class ExpirationUtil { public static String getExpirationDisplayValue(Context context, int expirationTime) { @@ -28,20 +28,20 @@ public class ExpirationUtil { } } - public static String getExpirationAbbreviatedDisplayValue(Context context, int expirationTime) { + public static String getExpirationAbbreviatedDisplayValue(Context context, long expirationTime) { if (expirationTime < TimeUnit.MINUTES.toSeconds(1)) { return context.getResources().getString(R.string.expiration_seconds_abbreviated, expirationTime); } else if (expirationTime < TimeUnit.HOURS.toSeconds(1)) { - int minutes = expirationTime / (int)TimeUnit.MINUTES.toSeconds(1); + long minutes = expirationTime / TimeUnit.MINUTES.toSeconds(1); return context.getResources().getString(R.string.expiration_minutes_abbreviated, minutes); } else if (expirationTime < TimeUnit.DAYS.toSeconds(1)) { - int hours = expirationTime / (int)TimeUnit.HOURS.toSeconds(1); + long hours = expirationTime / TimeUnit.HOURS.toSeconds(1); return context.getResources().getString(R.string.expiration_hours_abbreviated, hours); } else if (expirationTime < TimeUnit.DAYS.toSeconds(7)) { - int days = expirationTime / (int)TimeUnit.DAYS.toSeconds(1); + long days = expirationTime / TimeUnit.DAYS.toSeconds(1); return context.getResources().getString(R.string.expiration_days_abbreviated, days); } else { - int weeks = expirationTime / (int)TimeUnit.DAYS.toSeconds(7); + long weeks = expirationTime / TimeUnit.DAYS.toSeconds(7); return context.getResources().getString(R.string.expiration_weeks_abbreviated, weeks); } } diff --git a/libsession/src/main/java/org/session/libsession/utilities/ExpirationUtilities.kt b/libsession/src/main/java/org/session/libsession/utilities/ExpirationUtilities.kt new file mode 100644 index 000000000..4941e0ae5 --- /dev/null +++ b/libsession/src/main/java/org/session/libsession/utilities/ExpirationUtilities.kt @@ -0,0 +1,29 @@ +package org.session.libsession.utilities + +import network.loki.messenger.libsession_util.util.ExpiryMode +import org.session.libsignal.protos.SignalServiceProtos +import kotlin.reflect.KClass + +fun ExpiryMode?.typeRadioIndex(): Int { + return when (this) { + is ExpiryMode.AfterRead -> SignalServiceProtos.Content.ExpirationType.DELETE_AFTER_READ_VALUE + is ExpiryMode.AfterSend -> SignalServiceProtos.Content.ExpirationType.DELETE_AFTER_SEND_VALUE + else -> -1 + } +} + +fun SignalServiceProtos.Content.ExpirationType?.expiryMode(durationSeconds: Long): ExpiryMode? = when (this) { + null -> null + SignalServiceProtos.Content.ExpirationType.DELETE_AFTER_READ -> ExpiryMode.AfterRead(durationSeconds) + SignalServiceProtos.Content.ExpirationType.DELETE_AFTER_SEND -> ExpiryMode.AfterSend(durationSeconds) + SignalServiceProtos.Content.ExpirationType.UNKNOWN -> null +} + +fun Int.expiryType(): KClass? { + if (this == -1) return null + return when (this) { + SignalServiceProtos.Content.ExpirationType.DELETE_AFTER_READ_VALUE -> ExpiryMode.AfterSend::class + SignalServiceProtos.Content.ExpirationType.DELETE_AFTER_SEND_VALUE -> ExpiryMode.AfterRead::class + else -> ExpiryMode.NONE::class + } +} \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/utilities/SSKEnvironment.kt b/libsession/src/main/java/org/session/libsession/utilities/SSKEnvironment.kt index 463d69de4..5a6af4f7e 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/SSKEnvironment.kt +++ b/libsession/src/main/java/org/session/libsession/utilities/SSKEnvironment.kt @@ -1,6 +1,7 @@ package org.session.libsession.utilities import android.content.Context +import network.loki.messenger.libsession_util.util.ExpiryMode import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate import org.session.libsession.messaging.sending_receiving.notifications.MessageNotifier @@ -37,7 +38,7 @@ class SSKEnvironment( } interface MessageExpirationManagerProtocol { - fun setExpirationTimer(message: ExpirationTimerUpdate, expiryType: Int) + fun setExpirationTimer(message: ExpirationTimerUpdate, expiryType: ExpiryMode?) fun startAnyExpiration(timestamp: Long, author: String, expireStartedAt: Long) } diff --git a/libsignal/protobuf/SignalService.proto b/libsignal/protobuf/SignalService.proto index 90910c79f..cfb0f3ced 100644 --- a/libsignal/protobuf/SignalService.proto +++ b/libsignal/protobuf/SignalService.proto @@ -44,6 +44,7 @@ message UnsendRequest { message Content { enum ExpirationType { + UNKNOWN = 0; DELETE_AFTER_READ = 1; DELETE_AFTER_SEND = 2; } diff --git a/libsignal/src/main/java/org/session/libsignal/protos/SignalServiceProtos.java b/libsignal/src/main/java/org/session/libsignal/protos/SignalServiceProtos.java index 211eb0460..f02d9642b 100644 --- a/libsignal/src/main/java/org/session/libsignal/protos/SignalServiceProtos.java +++ b/libsignal/src/main/java/org/session/libsignal/protos/SignalServiceProtos.java @@ -2746,16 +2746,24 @@ public final class SignalServiceProtos { */ public enum ExpirationType implements com.google.protobuf.ProtocolMessageEnum { + /** + * UNKNOWN = 0; + */ + UNKNOWN(0, 0), /** * DELETE_AFTER_READ = 1; */ - DELETE_AFTER_READ(0, 1), + DELETE_AFTER_READ(1, 1), /** * DELETE_AFTER_SEND = 2; */ - DELETE_AFTER_SEND(1, 2), + DELETE_AFTER_SEND(2, 2), ; + /** + * UNKNOWN = 0; + */ + public static final int UNKNOWN_VALUE = 0; /** * DELETE_AFTER_READ = 1; */ @@ -2770,6 +2778,7 @@ public final class SignalServiceProtos { public static ExpirationType valueOf(int value) { switch (value) { + case 0: return UNKNOWN; case 1: return DELETE_AFTER_READ; case 2: return DELETE_AFTER_SEND; default: return null; @@ -3080,7 +3089,7 @@ public final class SignalServiceProtos { unsendRequest_ = org.session.libsignal.protos.SignalServiceProtos.UnsendRequest.getDefaultInstance(); messageRequestResponse_ = org.session.libsignal.protos.SignalServiceProtos.MessageRequestResponse.getDefaultInstance(); sharedConfigMessage_ = org.session.libsignal.protos.SignalServiceProtos.SharedConfigMessage.getDefaultInstance(); - expirationType_ = org.session.libsignal.protos.SignalServiceProtos.Content.ExpirationType.DELETE_AFTER_READ; + expirationType_ = org.session.libsignal.protos.SignalServiceProtos.Content.ExpirationType.UNKNOWN; expirationTimer_ = 0; lastDisappearingMessageChangeTimestamp_ = 0L; } @@ -3422,7 +3431,7 @@ public final class SignalServiceProtos { sharedConfigMessageBuilder_.clear(); } bitField0_ = (bitField0_ & ~0x00000100); - expirationType_ = org.session.libsignal.protos.SignalServiceProtos.Content.ExpirationType.DELETE_AFTER_READ; + expirationType_ = org.session.libsignal.protos.SignalServiceProtos.Content.ExpirationType.UNKNOWN; bitField0_ = (bitField0_ & ~0x00000200); expirationTimer_ = 0; bitField0_ = (bitField0_ & ~0x00000400); @@ -4727,7 +4736,7 @@ public final class SignalServiceProtos { } // optional .signalservice.Content.ExpirationType expirationType = 12; - private org.session.libsignal.protos.SignalServiceProtos.Content.ExpirationType expirationType_ = org.session.libsignal.protos.SignalServiceProtos.Content.ExpirationType.DELETE_AFTER_READ; + private org.session.libsignal.protos.SignalServiceProtos.Content.ExpirationType expirationType_ = org.session.libsignal.protos.SignalServiceProtos.Content.ExpirationType.UNKNOWN; /** * optional .signalservice.Content.ExpirationType expirationType = 12; */ @@ -4757,7 +4766,7 @@ public final class SignalServiceProtos { */ public Builder clearExpirationType() { bitField0_ = (bitField0_ & ~0x00000200); - expirationType_ = org.session.libsignal.protos.SignalServiceProtos.Content.ExpirationType.DELETE_AFTER_READ; + expirationType_ = org.session.libsignal.protos.SignalServiceProtos.Content.ExpirationType.UNKNOWN; onChanged(); return this; } @@ -27475,7 +27484,7 @@ public final class SignalServiceProtos { "\002(\004\0223\n\006action\030\002 \002(\0162#.signalservice.Typi" + "ngMessage.Action\"\"\n\006Action\022\013\n\007STARTED\020\000\022" + "\013\n\007STOPPED\020\001\"2\n\rUnsendRequest\022\021\n\ttimesta", - "mp\030\001 \002(\004\022\016\n\006author\030\002 \002(\t\"\356\005\n\007Content\022/\n\013" + + "mp\030\001 \002(\004\022\016\n\006author\030\002 \002(\t\"\373\005\n\007Content\022/\n\013" + "dataMessage\030\001 \001(\0132\032.signalservice.DataMe" + "ssage\022/\n\013callMessage\030\003 \001(\0132\032.signalservi" + "ce.CallMessage\0225\n\016receiptMessage\030\005 \001(\0132\035" + @@ -27492,104 +27501,104 @@ public final class SignalServiceProtos { "aredConfigMessage\022=\n\016expirationType\030\014 \001(" + "\0162%.signalservice.Content.ExpirationType" + "\022\027\n\017expirationTimer\030\r \001(\r\022.\n&lastDisappe" + - "aringMessageChangeTimestamp\030\016 \001(\004\">\n\016Exp" + - "irationType\022\025\n\021DELETE_AFTER_READ\020\001\022\025\n\021DE" + - "LETE_AFTER_SEND\020\002\"0\n\007KeyPair\022\021\n\tpublicKe", - "y\030\001 \002(\014\022\022\n\nprivateKey\030\002 \002(\014\"\226\001\n\032DataExtr" + - "actionNotification\022<\n\004type\030\001 \002(\0162..signa" + - "lservice.DataExtractionNotification.Type" + - "\022\021\n\ttimestamp\030\002 \001(\004\"\'\n\004Type\022\016\n\nSCREENSHO" + - "T\020\001\022\017\n\013MEDIA_SAVED\020\002\"\361\r\n\013DataMessage\022\014\n\004" + - "body\030\001 \001(\t\0225\n\013attachments\030\002 \003(\0132 .signal" + - "service.AttachmentPointer\022*\n\005group\030\003 \001(\013" + - "2\033.signalservice.GroupContext\022\r\n\005flags\030\004" + - " \001(\r\022\023\n\013expireTimer\030\005 \001(\r\022\022\n\nprofileKey\030" + - "\006 \001(\014\022\021\n\ttimestamp\030\007 \001(\004\022/\n\005quote\030\010 \001(\0132", - " .signalservice.DataMessage.Quote\0223\n\007pre" + - "view\030\n \003(\0132\".signalservice.DataMessage.P" + - "review\0225\n\010reaction\030\013 \001(\0132#.signalservice" + - ".DataMessage.Reaction\0227\n\007profile\030e \001(\0132&" + - ".signalservice.DataMessage.LokiProfile\022K" + - "\n\023openGroupInvitation\030f \001(\0132..signalserv" + - "ice.DataMessage.OpenGroupInvitation\022W\n\031c" + - "losedGroupControlMessage\030h \001(\01324.signals" + - "ervice.DataMessage.ClosedGroupControlMes" + - "sage\022\022\n\nsyncTarget\030i \001(\t\032\225\002\n\005Quote\022\n\n\002id", - "\030\001 \002(\004\022\016\n\006author\030\002 \002(\t\022\014\n\004text\030\003 \001(\t\022F\n\013" + - "attachments\030\004 \003(\01321.signalservice.DataMe" + - "ssage.Quote.QuotedAttachment\032\231\001\n\020QuotedA" + - "ttachment\022\023\n\013contentType\030\001 \001(\t\022\020\n\010fileNa" + - "me\030\002 \001(\t\0223\n\tthumbnail\030\003 \001(\0132 .signalserv" + - "ice.AttachmentPointer\022\r\n\005flags\030\004 \001(\r\"\032\n\005" + - "Flags\022\021\n\rVOICE_MESSAGE\020\001\032V\n\007Preview\022\013\n\003u" + - "rl\030\001 \002(\t\022\r\n\005title\030\002 \001(\t\022/\n\005image\030\003 \001(\0132 " + - ".signalservice.AttachmentPointer\032:\n\013Loki" + - "Profile\022\023\n\013displayName\030\001 \001(\t\022\026\n\016profileP", - "icture\030\002 \001(\t\0320\n\023OpenGroupInvitation\022\013\n\003u" + - "rl\030\001 \002(\t\022\014\n\004name\030\003 \002(\t\032\374\003\n\031ClosedGroupCo" + - "ntrolMessage\022G\n\004type\030\001 \002(\01629.signalservi" + - "ce.DataMessage.ClosedGroupControlMessage" + - ".Type\022\021\n\tpublicKey\030\002 \001(\014\022\014\n\004name\030\003 \001(\t\0221" + - "\n\021encryptionKeyPair\030\004 \001(\0132\026.signalservic" + - "e.KeyPair\022\017\n\007members\030\005 \003(\014\022\016\n\006admins\030\006 \003" + - "(\014\022U\n\010wrappers\030\007 \003(\0132C.signalservice.Dat" + - "aMessage.ClosedGroupControlMessage.KeyPa" + - "irWrapper\022\027\n\017expirationTimer\030\010 \001(\r\032=\n\016Ke", - "yPairWrapper\022\021\n\tpublicKey\030\001 \002(\014\022\030\n\020encry" + - "ptedKeyPair\030\002 \002(\014\"r\n\004Type\022\007\n\003NEW\020\001\022\027\n\023EN" + - "CRYPTION_KEY_PAIR\020\003\022\017\n\013NAME_CHANGE\020\004\022\021\n\r" + - "MEMBERS_ADDED\020\005\022\023\n\017MEMBERS_REMOVED\020\006\022\017\n\013" + - "MEMBER_LEFT\020\007\032\222\001\n\010Reaction\022\n\n\002id\030\001 \002(\004\022\016" + - "\n\006author\030\002 \002(\t\022\r\n\005emoji\030\003 \001(\t\022:\n\006action\030" + - "\004 \002(\0162*.signalservice.DataMessage.Reacti" + - "on.Action\"\037\n\006Action\022\t\n\005REACT\020\000\022\n\n\006REMOVE" + - "\020\001\"$\n\005Flags\022\033\n\027EXPIRATION_TIMER_UPDATE\020\002" + - "\"\352\001\n\013CallMessage\022-\n\004type\030\001 \002(\0162\037.signals", - "ervice.CallMessage.Type\022\014\n\004sdps\030\002 \003(\t\022\027\n" + - "\017sdpMLineIndexes\030\003 \003(\r\022\017\n\007sdpMids\030\004 \003(\t\022" + - "\014\n\004uuid\030\005 \002(\t\"f\n\004Type\022\r\n\tPRE_OFFER\020\006\022\t\n\005" + - "OFFER\020\001\022\n\n\006ANSWER\020\002\022\026\n\022PROVISIONAL_ANSWE" + - "R\020\003\022\022\n\016ICE_CANDIDATES\020\004\022\014\n\010END_CALL\020\005\"\245\004" + - "\n\024ConfigurationMessage\022E\n\014closedGroups\030\001" + - " \003(\0132/.signalservice.ConfigurationMessag" + - "e.ClosedGroup\022\022\n\nopenGroups\030\002 \003(\t\022\023\n\013dis" + - "playName\030\003 \001(\t\022\026\n\016profilePicture\030\004 \001(\t\022\022" + - "\n\nprofileKey\030\005 \001(\014\022=\n\010contacts\030\006 \003(\0132+.s", - "ignalservice.ConfigurationMessage.Contac" + - "t\032\233\001\n\013ClosedGroup\022\021\n\tpublicKey\030\001 \001(\014\022\014\n\004" + - "name\030\002 \001(\t\0221\n\021encryptionKeyPair\030\003 \001(\0132\026." + - "signalservice.KeyPair\022\017\n\007members\030\004 \003(\014\022\016" + - "\n\006admins\030\005 \003(\014\022\027\n\017expirationTimer\030\006 \001(\r\032" + - "\223\001\n\007Contact\022\021\n\tpublicKey\030\001 \002(\014\022\014\n\004name\030\002" + - " \002(\t\022\026\n\016profilePicture\030\003 \001(\t\022\022\n\nprofileK" + - "ey\030\004 \001(\014\022\022\n\nisApproved\030\005 \001(\010\022\021\n\tisBlocke" + - "d\030\006 \001(\010\022\024\n\014didApproveMe\030\007 \001(\010\"y\n\026Message" + - "RequestResponse\022\022\n\nisApproved\030\001 \002(\010\022\022\n\np", - "rofileKey\030\002 \001(\014\0227\n\007profile\030\003 \001(\0132&.signa" + - "lservice.DataMessage.LokiProfile\"\375\001\n\023Sha" + - "redConfigMessage\0225\n\004kind\030\001 \002(\0162\'.signals" + - "ervice.SharedConfigMessage.Kind\022\r\n\005seqno" + - "\030\002 \002(\003\022\014\n\004data\030\003 \002(\014\"\221\001\n\004Kind\022\020\n\014USER_PR" + - "OFILE\020\001\022\014\n\010CONTACTS\020\002\022\027\n\023CONVO_INFO_VOLA" + - "TILE\020\003\022\n\n\006GROUPS\020\004\022\025\n\021CLOSED_GROUP_INFO\020" + - "\005\022\030\n\024CLOSED_GROUP_MEMBERS\020\006\022\023\n\017ENCRYPTIO" + - "N_KEYS\020\007\"u\n\016ReceiptMessage\0220\n\004type\030\001 \002(\016" + - "2\".signalservice.ReceiptMessage.Type\022\021\n\t", - "timestamp\030\002 \003(\004\"\036\n\004Type\022\014\n\010DELIVERY\020\000\022\010\n" + - "\004READ\020\001\"\354\001\n\021AttachmentPointer\022\n\n\002id\030\001 \002(" + - "\006\022\023\n\013contentType\030\002 \001(\t\022\013\n\003key\030\003 \001(\014\022\014\n\004s" + - "ize\030\004 \001(\r\022\021\n\tthumbnail\030\005 \001(\014\022\016\n\006digest\030\006" + - " \001(\014\022\020\n\010fileName\030\007 \001(\t\022\r\n\005flags\030\010 \001(\r\022\r\n" + - "\005width\030\t \001(\r\022\016\n\006height\030\n \001(\r\022\017\n\007caption\030" + - "\013 \001(\t\022\013\n\003url\030e \001(\t\"\032\n\005Flags\022\021\n\rVOICE_MES" + - "SAGE\020\001\"\365\001\n\014GroupContext\022\n\n\002id\030\001 \001(\014\022.\n\004t" + - "ype\030\002 \001(\0162 .signalservice.GroupContext.T" + - "ype\022\014\n\004name\030\003 \001(\t\022\017\n\007members\030\004 \003(\t\0220\n\006av", - "atar\030\005 \001(\0132 .signalservice.AttachmentPoi" + - "nter\022\016\n\006admins\030\006 \003(\t\"H\n\004Type\022\013\n\007UNKNOWN\020" + - "\000\022\n\n\006UPDATE\020\001\022\013\n\007DELIVER\020\002\022\010\n\004QUIT\020\003\022\020\n\014" + - "REQUEST_INFO\020\004B3\n\034org.session.libsignal." + - "protosB\023SignalServiceProtos" + "aringMessageChangeTimestamp\030\016 \001(\004\"K\n\016Exp" + + "irationType\022\013\n\007UNKNOWN\020\000\022\025\n\021DELETE_AFTER" + + "_READ\020\001\022\025\n\021DELETE_AFTER_SEND\020\002\"0\n\007KeyPai", + "r\022\021\n\tpublicKey\030\001 \002(\014\022\022\n\nprivateKey\030\002 \002(\014" + + "\"\226\001\n\032DataExtractionNotification\022<\n\004type\030" + + "\001 \002(\0162..signalservice.DataExtractionNoti" + + "fication.Type\022\021\n\ttimestamp\030\002 \001(\004\"\'\n\004Type" + + "\022\016\n\nSCREENSHOT\020\001\022\017\n\013MEDIA_SAVED\020\002\"\361\r\n\013Da" + + "taMessage\022\014\n\004body\030\001 \001(\t\0225\n\013attachments\030\002" + + " \003(\0132 .signalservice.AttachmentPointer\022*" + + "\n\005group\030\003 \001(\0132\033.signalservice.GroupConte" + + "xt\022\r\n\005flags\030\004 \001(\r\022\023\n\013expireTimer\030\005 \001(\r\022\022" + + "\n\nprofileKey\030\006 \001(\014\022\021\n\ttimestamp\030\007 \001(\004\022/\n", + "\005quote\030\010 \001(\0132 .signalservice.DataMessage" + + ".Quote\0223\n\007preview\030\n \003(\0132\".signalservice." + + "DataMessage.Preview\0225\n\010reaction\030\013 \001(\0132#." + + "signalservice.DataMessage.Reaction\0227\n\007pr" + + "ofile\030e \001(\0132&.signalservice.DataMessage." + + "LokiProfile\022K\n\023openGroupInvitation\030f \001(\013" + + "2..signalservice.DataMessage.OpenGroupIn" + + "vitation\022W\n\031closedGroupControlMessage\030h " + + "\001(\01324.signalservice.DataMessage.ClosedGr" + + "oupControlMessage\022\022\n\nsyncTarget\030i \001(\t\032\225\002", + "\n\005Quote\022\n\n\002id\030\001 \002(\004\022\016\n\006author\030\002 \002(\t\022\014\n\004t" + + "ext\030\003 \001(\t\022F\n\013attachments\030\004 \003(\01321.signals" + + "ervice.DataMessage.Quote.QuotedAttachmen" + + "t\032\231\001\n\020QuotedAttachment\022\023\n\013contentType\030\001 " + + "\001(\t\022\020\n\010fileName\030\002 \001(\t\0223\n\tthumbnail\030\003 \001(\013" + + "2 .signalservice.AttachmentPointer\022\r\n\005fl" + + "ags\030\004 \001(\r\"\032\n\005Flags\022\021\n\rVOICE_MESSAGE\020\001\032V\n" + + "\007Preview\022\013\n\003url\030\001 \002(\t\022\r\n\005title\030\002 \001(\t\022/\n\005" + + "image\030\003 \001(\0132 .signalservice.AttachmentPo" + + "inter\032:\n\013LokiProfile\022\023\n\013displayName\030\001 \001(", + "\t\022\026\n\016profilePicture\030\002 \001(\t\0320\n\023OpenGroupIn" + + "vitation\022\013\n\003url\030\001 \002(\t\022\014\n\004name\030\003 \002(\t\032\374\003\n\031" + + "ClosedGroupControlMessage\022G\n\004type\030\001 \002(\0162" + + "9.signalservice.DataMessage.ClosedGroupC" + + "ontrolMessage.Type\022\021\n\tpublicKey\030\002 \001(\014\022\014\n" + + "\004name\030\003 \001(\t\0221\n\021encryptionKeyPair\030\004 \001(\0132\026" + + ".signalservice.KeyPair\022\017\n\007members\030\005 \003(\014\022" + + "\016\n\006admins\030\006 \003(\014\022U\n\010wrappers\030\007 \003(\0132C.sign" + + "alservice.DataMessage.ClosedGroupControl" + + "Message.KeyPairWrapper\022\027\n\017expirationTime", + "r\030\010 \001(\r\032=\n\016KeyPairWrapper\022\021\n\tpublicKey\030\001" + + " \002(\014\022\030\n\020encryptedKeyPair\030\002 \002(\014\"r\n\004Type\022\007" + + "\n\003NEW\020\001\022\027\n\023ENCRYPTION_KEY_PAIR\020\003\022\017\n\013NAME" + + "_CHANGE\020\004\022\021\n\rMEMBERS_ADDED\020\005\022\023\n\017MEMBERS_" + + "REMOVED\020\006\022\017\n\013MEMBER_LEFT\020\007\032\222\001\n\010Reaction\022" + + "\n\n\002id\030\001 \002(\004\022\016\n\006author\030\002 \002(\t\022\r\n\005emoji\030\003 \001" + + "(\t\022:\n\006action\030\004 \002(\0162*.signalservice.DataM" + + "essage.Reaction.Action\"\037\n\006Action\022\t\n\005REAC" + + "T\020\000\022\n\n\006REMOVE\020\001\"$\n\005Flags\022\033\n\027EXPIRATION_T" + + "IMER_UPDATE\020\002\"\352\001\n\013CallMessage\022-\n\004type\030\001 ", + "\002(\0162\037.signalservice.CallMessage.Type\022\014\n\004" + + "sdps\030\002 \003(\t\022\027\n\017sdpMLineIndexes\030\003 \003(\r\022\017\n\007s" + + "dpMids\030\004 \003(\t\022\014\n\004uuid\030\005 \002(\t\"f\n\004Type\022\r\n\tPR" + + "E_OFFER\020\006\022\t\n\005OFFER\020\001\022\n\n\006ANSWER\020\002\022\026\n\022PROV" + + "ISIONAL_ANSWER\020\003\022\022\n\016ICE_CANDIDATES\020\004\022\014\n\010" + + "END_CALL\020\005\"\245\004\n\024ConfigurationMessage\022E\n\014c" + + "losedGroups\030\001 \003(\0132/.signalservice.Config" + + "urationMessage.ClosedGroup\022\022\n\nopenGroups" + + "\030\002 \003(\t\022\023\n\013displayName\030\003 \001(\t\022\026\n\016profilePi" + + "cture\030\004 \001(\t\022\022\n\nprofileKey\030\005 \001(\014\022=\n\010conta", + "cts\030\006 \003(\0132+.signalservice.ConfigurationM" + + "essage.Contact\032\233\001\n\013ClosedGroup\022\021\n\tpublic" + + "Key\030\001 \001(\014\022\014\n\004name\030\002 \001(\t\0221\n\021encryptionKey" + + "Pair\030\003 \001(\0132\026.signalservice.KeyPair\022\017\n\007me" + + "mbers\030\004 \003(\014\022\016\n\006admins\030\005 \003(\014\022\027\n\017expiratio" + + "nTimer\030\006 \001(\r\032\223\001\n\007Contact\022\021\n\tpublicKey\030\001 " + + "\002(\014\022\014\n\004name\030\002 \002(\t\022\026\n\016profilePicture\030\003 \001(" + + "\t\022\022\n\nprofileKey\030\004 \001(\014\022\022\n\nisApproved\030\005 \001(" + + "\010\022\021\n\tisBlocked\030\006 \001(\010\022\024\n\014didApproveMe\030\007 \001" + + "(\010\"y\n\026MessageRequestResponse\022\022\n\nisApprov", + "ed\030\001 \002(\010\022\022\n\nprofileKey\030\002 \001(\014\0227\n\007profile\030" + + "\003 \001(\0132&.signalservice.DataMessage.LokiPr" + + "ofile\"\375\001\n\023SharedConfigMessage\0225\n\004kind\030\001 " + + "\002(\0162\'.signalservice.SharedConfigMessage." + + "Kind\022\r\n\005seqno\030\002 \002(\003\022\014\n\004data\030\003 \002(\014\"\221\001\n\004Ki" + + "nd\022\020\n\014USER_PROFILE\020\001\022\014\n\010CONTACTS\020\002\022\027\n\023CO" + + "NVO_INFO_VOLATILE\020\003\022\n\n\006GROUPS\020\004\022\025\n\021CLOSE" + + "D_GROUP_INFO\020\005\022\030\n\024CLOSED_GROUP_MEMBERS\020\006" + + "\022\023\n\017ENCRYPTION_KEYS\020\007\"u\n\016ReceiptMessage\022" + + "0\n\004type\030\001 \002(\0162\".signalservice.ReceiptMes", + "sage.Type\022\021\n\ttimestamp\030\002 \003(\004\"\036\n\004Type\022\014\n\010" + + "DELIVERY\020\000\022\010\n\004READ\020\001\"\354\001\n\021AttachmentPoint" + + "er\022\n\n\002id\030\001 \002(\006\022\023\n\013contentType\030\002 \001(\t\022\013\n\003k" + + "ey\030\003 \001(\014\022\014\n\004size\030\004 \001(\r\022\021\n\tthumbnail\030\005 \001(" + + "\014\022\016\n\006digest\030\006 \001(\014\022\020\n\010fileName\030\007 \001(\t\022\r\n\005f" + + "lags\030\010 \001(\r\022\r\n\005width\030\t \001(\r\022\016\n\006height\030\n \001(" + + "\r\022\017\n\007caption\030\013 \001(\t\022\013\n\003url\030e \001(\t\"\032\n\005Flags" + + "\022\021\n\rVOICE_MESSAGE\020\001\"\365\001\n\014GroupContext\022\n\n\002" + + "id\030\001 \001(\014\022.\n\004type\030\002 \001(\0162 .signalservice.G" + + "roupContext.Type\022\014\n\004name\030\003 \001(\t\022\017\n\007member", + "s\030\004 \003(\t\0220\n\006avatar\030\005 \001(\0132 .signalservice." + + "AttachmentPointer\022\016\n\006admins\030\006 \003(\t\"H\n\004Typ" + + "e\022\013\n\007UNKNOWN\020\000\022\n\n\006UPDATE\020\001\022\013\n\007DELIVER\020\002\022" + + "\010\n\004QUIT\020\003\022\020\n\014REQUEST_INFO\020\004B3\n\034org.sessi" + + "on.libsignal.protosB\023SignalServiceProtos" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {