mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-21 15:05:19 +00:00
Fix Message#expiryMode de/serialisation
This commit is contained in:
parent
cb0327ecb2
commit
4c7485f53d
@ -14,6 +14,7 @@ import org.session.libsession.utilities.ExpirationUtil
|
||||
import org.session.libsession.utilities.SSKEnvironment.MessageExpirationManagerProtocol
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsession.utilities.getExpirationTypeDisplayValue
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||
import org.thoughtcrime.securesms.showSessionDialog
|
||||
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
|
||||
@ -25,11 +26,11 @@ class DisappearingMessages @Inject constructor(
|
||||
private val textSecurePreferences: TextSecurePreferences,
|
||||
private val messageExpirationManager: MessageExpirationManagerProtocol,
|
||||
) {
|
||||
fun set(threadId: Long, address: Address, mode: ExpiryMode) {
|
||||
fun set(threadId: Long, address: Address, mode: ExpiryMode, isGroup: Boolean) {
|
||||
val expiryChangeTimestampMs = SnodeAPI.nowWithOffset
|
||||
MessagingModuleConfiguration.shared.storage.setExpirationConfiguration(ExpirationConfiguration(threadId, mode, expiryChangeTimestampMs))
|
||||
|
||||
val message = ExpirationTimerUpdate().apply {
|
||||
val message = ExpirationTimerUpdate(isGroup = isGroup).apply {
|
||||
expiryMode = mode
|
||||
sender = textSecurePreferences.getLocalNumber()
|
||||
isSenderSelf = true
|
||||
@ -62,7 +63,7 @@ class DisappearingMessages @Inject constructor(
|
||||
text = if (message.expiresIn == 0L) R.string.dialog_disappearing_messages_follow_setting_confirm else R.string.dialog_disappearing_messages_follow_setting_set,
|
||||
contentDescription = if (message.expiresIn == 0L) R.string.AccessibilityId_confirm else R.string.AccessibilityId_set_button
|
||||
) {
|
||||
set(message.threadId, message.recipient.address, message.expiryMode)
|
||||
set(message.threadId, message.recipient.address, message.expiryMode, message.recipient.isClosedGroupRecipient)
|
||||
}
|
||||
cancelButton()
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ class DisappearingMessagesViewModel(
|
||||
return@launch
|
||||
}
|
||||
|
||||
disappearingMessages.set(threadId, address, mode)
|
||||
disappearingMessages.set(threadId, address, mode, state.isGroup)
|
||||
|
||||
_event.send(Event.SUCCESS)
|
||||
}
|
||||
|
@ -72,6 +72,7 @@ import org.session.libsession.messaging.jobs.JobQueue
|
||||
import org.session.libsession.messaging.mentions.Mention
|
||||
import org.session.libsession.messaging.mentions.MentionsManager
|
||||
import org.session.libsession.messaging.messages.ExpirationConfiguration
|
||||
import org.session.libsession.messaging.messages.applyExpiryMode
|
||||
import org.session.libsession.messaging.messages.control.DataExtractionNotification
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage
|
||||
@ -1574,7 +1575,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
return null
|
||||
}
|
||||
// Create the message
|
||||
val message = VisibleMessage()
|
||||
val message = VisibleMessage().applyExpiryMode(viewModel.threadId)
|
||||
message.sentTimestamp = sentTimestamp
|
||||
message.text = text
|
||||
val expiresInMillis = viewModel.expirationConfiguration?.expiryMode?.expiryMillis ?: 0
|
||||
@ -1609,7 +1610,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
||||
val sentTimestamp = SnodeAPI.nowWithOffset
|
||||
processMessageRequestApproval()
|
||||
// Create the message
|
||||
val message = VisibleMessage()
|
||||
val message = VisibleMessage().applyExpiryMode()
|
||||
message.sentTimestamp = sentTimestamp
|
||||
message.text = body
|
||||
val quote = quotedMessage?.let {
|
||||
|
@ -30,6 +30,10 @@ class ExpirationTimerView @JvmOverloads constructor(
|
||||
R.drawable.timer60
|
||||
)
|
||||
|
||||
fun setTimerIcon() {
|
||||
setExpirationTime(0L, 0L)
|
||||
}
|
||||
|
||||
fun setExpirationTime(startedAt: Long, expiresIn: Long) {
|
||||
if (expiresIn == 0L) {
|
||||
setImageResource(R.drawable.timer55)
|
||||
|
@ -53,12 +53,19 @@ class ControlMessageView : LinearLayout {
|
||||
|
||||
Log.d(TAG, "bind() called, messageBody = $messageBody")
|
||||
|
||||
expirationTimerView.setExpirationTime(message.expireStarted, message.expiresIn)
|
||||
val threadRecipient = DatabaseComponent.get(context).threadDatabase().getRecipientForThreadId(message.threadId)
|
||||
|
||||
if (threadRecipient?.isClosedGroupRecipient == true) {
|
||||
expirationTimerView.setTimerIcon()
|
||||
} else {
|
||||
expirationTimerView.setExpirationTime(message.expireStarted, message.expiresIn)
|
||||
}
|
||||
|
||||
|
||||
followSetting.isVisible = ExpirationConfiguration.isNewConfigEnabled
|
||||
&& !message.isOutgoing
|
||||
&& message.expiryMode != (MessagingModuleConfiguration.shared.storage.getExpirationConfiguration(message.threadId)?.expiryMode ?: ExpiryMode.NONE)
|
||||
&& DatabaseComponent.get(context).threadDatabase().getRecipientForThreadId(message.threadId)?.isGroupRecipient != true
|
||||
&& threadRecipient?.isGroupRecipient != true
|
||||
|
||||
followSetting.setOnClickListener { disappearingMessages.showFollowSettingDialog(context, message) }
|
||||
}
|
||||
|
@ -4,11 +4,11 @@ import org.thoughtcrime.securesms.conversation.disappearingmessages.ExpiryType
|
||||
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId
|
||||
|
||||
data class MarkedMessageInfo(val syncMessageId: SyncMessageId, val expirationInfo: ExpirationInfo) {
|
||||
fun guessExpiryType(): ExpiryType = expirationInfo.run {
|
||||
when {
|
||||
syncMessageId.timetamp == expireStarted -> ExpiryType.AFTER_SEND
|
||||
expiresIn > 0 -> ExpiryType.AFTER_READ
|
||||
else -> ExpiryType.NONE
|
||||
}
|
||||
val expiryType get() = when {
|
||||
syncMessageId.timetamp == expirationInfo.expireStarted -> ExpiryType.AFTER_SEND
|
||||
expirationInfo.expiresIn > 0 -> ExpiryType.AFTER_READ
|
||||
else -> ExpiryType.NONE
|
||||
}
|
||||
|
||||
val expiryMode get() = expiryType.mode(expirationInfo.expiresIn)
|
||||
}
|
||||
|
@ -382,9 +382,6 @@ open class Storage(
|
||||
DatabaseComponent.get(context).lokiMessageDatabase().setMessageServerHash(id, message.isMediaMessage(), serverHash)
|
||||
}
|
||||
}
|
||||
if (expiryMode is ExpiryMode.AfterSend) {
|
||||
SSKEnvironment.shared.messageExpirationManager.startAnyExpiration(message.sentTimestamp!!, message.sender!!, expireStartedAt)
|
||||
}
|
||||
return messageID
|
||||
}
|
||||
|
||||
@ -972,26 +969,24 @@ open class Storage(
|
||||
val recipient = Recipient.from(context, fromSerialized(groupID), false)
|
||||
val threadId = DatabaseComponent.get(context).threadDatabase().getOrCreateThreadIdFor(recipient)
|
||||
val expirationConfig = getExpirationConfiguration(threadId)
|
||||
val expiryMode = expirationConfig?.expiryMode
|
||||
val expiresInMillis = expiryMode?.expiryMillis ?: 0
|
||||
val expiryMode = expirationConfig?.expiryMode ?: ExpiryMode.NONE
|
||||
val expiresInMillis = expiryMode.expiryMillis
|
||||
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 (expiryMode is ExpiryMode.AfterSend) {
|
||||
SSKEnvironment.shared.messageExpirationManager.startAnyExpiration(sentTimestamp, senderPublicKey, expireStartedAt)
|
||||
}
|
||||
SSKEnvironment.shared.messageExpirationManager.maybeStartExpiration(sentTimestamp, senderPublicKey, expiryMode)
|
||||
}
|
||||
|
||||
override fun insertOutgoingInfoMessage(context: Context, groupID: String, type: SignalServiceGroup.Type, name: String, members: Collection<String>, admins: Collection<String>, threadID: Long, sentTimestamp: Long) {
|
||||
val userPublicKey = getUserPublicKey()
|
||||
val userPublicKey = getUserPublicKey()!!
|
||||
val recipient = Recipient.from(context, fromSerialized(groupID), false)
|
||||
val threadId = DatabaseComponent.get(context).threadDatabase().getOrCreateThreadIdFor(recipient)
|
||||
val expirationConfig = getExpirationConfiguration(threadId)
|
||||
val expiryMode = expirationConfig?.expiryMode
|
||||
val expiresInMillis = expiryMode?.expiryMillis ?: 0
|
||||
val expiryMode = expirationConfig?.expiryMode ?: ExpiryMode.NONE
|
||||
val expiresInMillis = expiryMode.expiryMillis
|
||||
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())
|
||||
@ -1000,9 +995,7 @@ open class Storage(
|
||||
if (mmsSmsDB.getMessageFor(sentTimestamp, userPublicKey) != null) return
|
||||
val infoMessageID = mmsDB.insertMessageOutbox(infoMessage, threadID, false, null, runThreadUpdate = true)
|
||||
mmsDB.markAsSent(infoMessageID, true)
|
||||
if (expiryMode is ExpiryMode.AfterSend) {
|
||||
SSKEnvironment.shared.messageExpirationManager.startAnyExpiration(sentTimestamp, userPublicKey!!, expireStartedAt)
|
||||
}
|
||||
SSKEnvironment.shared.messageExpirationManager.maybeStartExpiration(sentTimestamp, userPublicKey, expiryMode)
|
||||
}
|
||||
|
||||
override fun isClosedGroup(publicKey: String): Boolean {
|
||||
@ -1403,8 +1396,8 @@ open class Storage(
|
||||
if (recipient.isBlocked) return
|
||||
val threadId = getThreadId(recipient) ?: return
|
||||
val expirationConfig = getExpirationConfiguration(threadId)
|
||||
val expiryMode = expirationConfig?.expiryMode
|
||||
val expiresInMillis = expiryMode?.expiryMillis ?: 0
|
||||
val expiryMode = expirationConfig?.expiryMode ?: ExpiryMode.NONE
|
||||
val expiresInMillis = expiryMode.expiryMillis
|
||||
val expireStartedAt = if (expiryMode is ExpiryMode.AfterSend) sentTimestamp else 0
|
||||
val mediaMessage = IncomingMediaMessage(
|
||||
address,
|
||||
@ -1426,9 +1419,8 @@ open class Storage(
|
||||
)
|
||||
|
||||
database.insertSecureDecryptedMessageInbox(mediaMessage, threadId, runThreadUpdate = true)
|
||||
if (expiryMode is ExpiryMode.AfterSend) {
|
||||
SSKEnvironment.shared.messageExpirationManager.startAnyExpiration(sentTimestamp, senderPublicKey, expireStartedAt)
|
||||
}
|
||||
|
||||
SSKEnvironment.shared.messageExpirationManager.maybeStartExpiration(sentTimestamp, senderPublicKey, expiryMode)
|
||||
}
|
||||
|
||||
override fun insertMessageRequestResponse(response: MessageRequestResponse) {
|
||||
@ -1557,14 +1549,12 @@ open class Storage(
|
||||
val recipient = Recipient.from(context, address, false)
|
||||
val threadId = DatabaseComponent.get(context).threadDatabase().getOrCreateThreadIdFor(recipient)
|
||||
val expirationConfig = getExpirationConfiguration(threadId)
|
||||
val expiryMode = expirationConfig?.expiryMode
|
||||
val expiresInMillis = expiryMode?.expiryMillis ?: 0
|
||||
val expiryMode = expirationConfig?.expiryMode?.coerceSendToRead() ?: ExpiryMode.NONE
|
||||
val expiresInMillis = expiryMode.expiryMillis
|
||||
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 (expiryMode is ExpiryMode.AfterSend) {
|
||||
SSKEnvironment.shared.messageExpirationManager.startAnyExpiration(sentTimestamp, senderPublicKey, expireStartedAt)
|
||||
}
|
||||
SSKEnvironment.shared.messageExpirationManager.maybeStartExpiration(sentTimestamp, senderPublicKey, expiryMode)
|
||||
}
|
||||
|
||||
override fun conversationHasOutgoing(userPublicKey: String): Boolean {
|
||||
|
@ -11,6 +11,7 @@ import org.session.libsession.messaging.messages.control.ReadReceipt
|
||||
import org.session.libsession.messaging.sending_receiving.MessageSender.send
|
||||
import org.session.libsession.snode.SnodeAPI
|
||||
import org.session.libsession.snode.SnodeAPI.nowWithOffset
|
||||
import org.session.libsession.utilities.SSKEnvironment
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsession.utilities.TextSecurePreferences.Companion.isReadReceiptsEnabled
|
||||
import org.session.libsession.utilities.associateByNotNull
|
||||
@ -62,6 +63,18 @@ class MarkReadReceiver : BroadcastReceiver() {
|
||||
|
||||
sendReadReceipts(context, markedReadMessages)
|
||||
|
||||
markedReadMessages
|
||||
.filter { it.expiryType == ExpiryType.AFTER_READ }
|
||||
.forEach { info ->
|
||||
DatabaseComponent.get(context).mmsSmsDatabase().getMessageForTimestamp(info.syncMessageId.timetamp)
|
||||
?.takeUnless { it.isExpirationTimerUpdate && it.recipient.isClosedGroupRecipient }
|
||||
?.run {
|
||||
SSKEnvironment.shared.messageExpirationManager.startDisappearAfterRead(
|
||||
info.syncMessageId.timetamp,
|
||||
info.syncMessageId.address.serialize()
|
||||
)
|
||||
}
|
||||
}
|
||||
markedReadMessages.forEach { scheduleDeletion(context, it.expirationInfo) }
|
||||
|
||||
hashToDisappearAfterReadMessage(context, markedReadMessages)?.let {
|
||||
@ -77,7 +90,7 @@ class MarkReadReceiver : BroadcastReceiver() {
|
||||
val loki = DatabaseComponent.get(context).lokiMessageDatabase()
|
||||
|
||||
return markedReadMessages
|
||||
.filter { it.guessExpiryType() == ExpiryType.AFTER_READ }
|
||||
.filter { it.expiryType == ExpiryType.AFTER_READ }
|
||||
.associateByNotNull { it.expirationInfo.run { loki.getMessageServerHash(id, isMms) } }
|
||||
.takeIf { it.isNotEmpty() }
|
||||
}
|
||||
|
@ -49,6 +49,9 @@ class ExpiringMessageManager(context: Context) : MessageExpirationManagerProtoco
|
||||
|
||||
fun scheduleDeletion(id: Long, mms: Boolean, startedAtTimestamp: Long, expiresInMillis: Long) {
|
||||
Log.d(TAG, "scheduleDeletion() called with: id = $id, mms = $mms, startedAtTimestamp = $startedAtTimestamp, expiresInMillis = $expiresInMillis")
|
||||
|
||||
if (startedAtTimestamp <= 0) return
|
||||
|
||||
val expiresAtMillis = startedAtTimestamp + expiresInMillis
|
||||
synchronized(expiringMessageReferences) {
|
||||
expiringMessageReferences += ExpiringMessageReference(id, mms, expiresAtMillis)
|
||||
@ -164,16 +167,16 @@ class ExpiringMessageManager(context: Context) : MessageExpirationManagerProtoco
|
||||
insertIncomingExpirationTimerMessage(message, expireStartedAt)
|
||||
}
|
||||
|
||||
startAnyExpiration(message)
|
||||
maybeStartExpiration(message)
|
||||
}
|
||||
|
||||
override fun startAnyExpiration(timestamp: Long, author: String, expireStartedAt: Long) {
|
||||
Log.d(TAG, "startAnyExpiration() called with: timestamp = $timestamp, author = $author, expireStartedAt = $expireStartedAt")
|
||||
val messageRecord = mmsSmsDatabase.getMessageFor(timestamp, author) ?: throw Exception("no message record!!!")
|
||||
Log.d(TAG, "startAnyExpiration() $messageRecord")
|
||||
val mms = messageRecord.isMms()
|
||||
getDatabase(mms).markExpireStarted(messageRecord.getId(), expireStartedAt)
|
||||
scheduleDeletion(messageRecord.getId(), mms, expireStartedAt, messageRecord.expiresIn)
|
||||
|
||||
mmsSmsDatabase.getMessageFor(timestamp, author)?.run {
|
||||
getDatabase(isMms()).markExpireStarted(getId(), expireStartedAt)
|
||||
scheduleDeletion(getId(), isMms(), expireStartedAt, expiresIn)
|
||||
} ?: Log.e(TAG, "no message record!!!")
|
||||
}
|
||||
|
||||
private inner class LoadTask : Runnable {
|
||||
|
@ -16,6 +16,7 @@ import nl.komponents.kovenant.Promise
|
||||
import nl.komponents.kovenant.functional.bind
|
||||
import org.session.libsession.database.StorageProtocol
|
||||
import org.session.libsession.messaging.calls.CallMessageType
|
||||
import org.session.libsession.messaging.messages.applyExpiryMode
|
||||
import org.session.libsession.messaging.messages.control.CallMessage
|
||||
import org.session.libsession.messaging.sending_receiving.MessageSender
|
||||
import org.session.libsession.snode.SnodeAPI
|
||||
@ -57,8 +58,12 @@ import java.util.ArrayDeque
|
||||
import java.util.UUID
|
||||
import org.thoughtcrime.securesms.webrtc.data.State as CallState
|
||||
|
||||
class CallManager(context: Context, audioManager: AudioManagerCompat, private val storage: StorageProtocol): PeerConnection.Observer,
|
||||
SignalAudioManager.EventListener, CameraEventListener, DataChannel.Observer {
|
||||
class CallManager(
|
||||
private val context: Context,
|
||||
audioManager: AudioManagerCompat,
|
||||
private val storage: StorageProtocol
|
||||
): PeerConnection.Observer,
|
||||
SignalAudioManager.EventListener, CameraEventListener, DataChannel.Observer {
|
||||
|
||||
sealed class StateEvent {
|
||||
data class AudioEnabled(val isEnabled: Boolean): StateEvent()
|
||||
@ -293,17 +298,16 @@ class CallManager(context: Context, audioManager: AudioManagerCompat, private va
|
||||
while (pendingOutgoingIceUpdates.isNotEmpty()) {
|
||||
currentPendings.add(pendingOutgoingIceUpdates.pop())
|
||||
}
|
||||
val sdps = currentPendings.map { it.sdp }
|
||||
val sdpMLineIndexes = currentPendings.map { it.sdpMLineIndex }
|
||||
val sdpMids = currentPendings.map { it.sdpMid }
|
||||
|
||||
MessageSender.sendNonDurably(CallMessage(
|
||||
ICE_CANDIDATES,
|
||||
sdps = sdps,
|
||||
sdpMLineIndexes = sdpMLineIndexes,
|
||||
sdpMids = sdpMids,
|
||||
currentCallId
|
||||
), currentRecipient.address, isSyncMessage = currentRecipient.isLocalNumber)
|
||||
CallMessage(
|
||||
ICE_CANDIDATES,
|
||||
sdps = currentPendings.map(IceCandidate::sdp),
|
||||
sdpMLineIndexes = currentPendings.map(IceCandidate::sdpMLineIndex),
|
||||
sdpMids = currentPendings.map(IceCandidate::sdpMid),
|
||||
currentCallId
|
||||
)
|
||||
.applyExpiryMode()
|
||||
.also { MessageSender.sendNonDurably(it, currentRecipient.address, isSyncMessage = currentRecipient.isLocalNumber) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,11 +4,13 @@ import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
sealed class ExpiryMode(val expirySeconds: Long) {
|
||||
object NONE: ExpiryMode(0)
|
||||
data class Legacy(private val seconds: Long): ExpiryMode(seconds) // after read
|
||||
data class Legacy(private val seconds: Long): ExpiryMode(seconds)
|
||||
data class AfterSend(private val seconds: Long): ExpiryMode(seconds)
|
||||
data class AfterRead(private val seconds: Long): ExpiryMode(seconds)
|
||||
|
||||
val duration get() = expirySeconds.seconds
|
||||
|
||||
val expiryMillis get() = expirySeconds * 1000L
|
||||
|
||||
fun coerceSendToRead(coerce: Boolean = true) = if (coerce && this is AfterSend) AfterRead(expirySeconds) else this
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ 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.Address
|
||||
import org.session.libsession.utilities.GroupUtil
|
||||
import org.session.libsignal.protos.SignalServiceProtos
|
||||
import org.session.libsignal.protos.SignalServiceProtos.Content.ExpirationType
|
||||
@ -25,6 +26,8 @@ abstract class Message {
|
||||
|
||||
var expiryMode: ExpiryMode = ExpiryMode.NONE
|
||||
|
||||
open val coerceDisappearAfterSendToRead = false
|
||||
|
||||
open val defaultTtl: Long = 14 * 24 * 60 * 60 * 1000
|
||||
open val ttl: Long get() = specifiedTtl ?: defaultTtl
|
||||
open val isSelfSendValid: Boolean = false
|
||||
@ -51,26 +54,16 @@ abstract class Message {
|
||||
abstract fun toProto(): SignalServiceProtos.Content?
|
||||
|
||||
fun setGroupContext(dataMessage: SignalServiceProtos.DataMessage.Builder) {
|
||||
val groupProto = SignalServiceProtos.GroupContext.newBuilder()
|
||||
val groupID = GroupUtil.doubleEncodeGroupID(recipient!!)
|
||||
groupProto.id = ByteString.copyFrom(GroupUtil.getDecodedGroupIDAsData(groupID))
|
||||
groupProto.type = SignalServiceProtos.GroupContext.Type.DELIVER
|
||||
dataMessage.group = groupProto.build()
|
||||
dataMessage.group = SignalServiceProtos.GroupContext.newBuilder().apply {
|
||||
id = GroupUtil.doubleEncodeGroupID(recipient!!).let(GroupUtil::getDecodedGroupIDAsData).let(ByteString::copyFrom)
|
||||
type = SignalServiceProtos.GroupContext.Type.DELIVER
|
||||
}.build()
|
||||
}
|
||||
|
||||
fun SignalServiceProtos.Content.Builder.setExpirationConfigurationIfNeeded(
|
||||
threadId: Long?,
|
||||
coerceDisappearAfterSendToRead: Boolean = false
|
||||
): SignalServiceProtos.Content.Builder {
|
||||
val config = threadId?.let(MessagingModuleConfiguration.shared.storage::getExpirationConfiguration)
|
||||
?: run {
|
||||
expirationTimer = 0
|
||||
return this
|
||||
}
|
||||
expirationTimer = config.expiryMode.expirySeconds.toInt()
|
||||
lastDisappearingMessageChangeTimestamp = config.updatedTimestampMs
|
||||
expirationType = when (config.expiryMode) {
|
||||
is ExpiryMode.AfterSend -> if (coerceDisappearAfterSendToRead) ExpirationType.DELETE_AFTER_READ else ExpirationType.DELETE_AFTER_SEND
|
||||
fun SignalServiceProtos.Content.Builder.applyExpiryMode(): SignalServiceProtos.Content.Builder {
|
||||
expirationTimer = expiryMode.expirySeconds.toInt()
|
||||
expirationType = when (expiryMode) {
|
||||
is ExpiryMode.AfterSend -> ExpirationType.DELETE_AFTER_SEND
|
||||
is ExpiryMode.AfterRead -> ExpirationType.DELETE_AFTER_READ
|
||||
else -> ExpirationType.UNKNOWN
|
||||
}
|
||||
@ -79,13 +72,36 @@ abstract class Message {
|
||||
}
|
||||
|
||||
inline fun <reified M: Message> M.copyExpiration(proto: SignalServiceProtos.Content): M {
|
||||
val duration: Int = (if (proto.hasExpirationTimer()) proto.expirationTimer else if (proto.hasDataMessage()) proto.dataMessage?.expireTimer else null) ?: return this
|
||||
|
||||
expiryMode = when (proto.expirationType.takeIf { duration > 0 }) {
|
||||
ExpirationType.DELETE_AFTER_SEND -> ExpiryMode.AfterSend(duration.toLong())
|
||||
ExpirationType.DELETE_AFTER_READ -> ExpiryMode.AfterRead(duration.toLong())
|
||||
else -> ExpiryMode.NONE
|
||||
(proto.takeIf { it.hasExpirationTimer() }?.expirationTimer ?: proto.dataMessage?.expireTimer)?.let { duration ->
|
||||
expiryMode = when (proto.expirationType.takeIf { duration > 0 }) {
|
||||
ExpirationType.DELETE_AFTER_SEND -> ExpiryMode.AfterSend(duration.toLong())
|
||||
ExpirationType.DELETE_AFTER_READ -> ExpiryMode.AfterRead(duration.toLong())
|
||||
else -> ExpiryMode.NONE
|
||||
}
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
fun SignalServiceProtos.Content.expiryMode(): ExpiryMode =
|
||||
(takeIf { it.hasExpirationTimer() }?.expirationTimer ?: dataMessage?.expireTimer)?.let { duration ->
|
||||
when (expirationType.takeIf { duration > 0 }) {
|
||||
ExpirationType.DELETE_AFTER_SEND -> ExpiryMode.AfterSend(duration.toLong())
|
||||
ExpirationType.DELETE_AFTER_READ -> ExpiryMode.AfterRead(duration.toLong())
|
||||
else -> ExpiryMode.NONE
|
||||
}
|
||||
} ?: ExpiryMode.NONE
|
||||
|
||||
/**
|
||||
* Apply ExpiryMode from the current setting.
|
||||
*/
|
||||
inline fun <reified M: Message> M.applyExpiryMode(): M {
|
||||
val address = Address.fromSerialized(sender ?: return this)
|
||||
MessagingModuleConfiguration.shared.storage.getThreadId(address)?.let(::applyExpiryMode)
|
||||
return this
|
||||
}
|
||||
|
||||
inline fun <reified M: Message> M.applyExpiryMode(thread: Long): M {
|
||||
val storage = MessagingModuleConfiguration.shared.storage
|
||||
expiryMode = storage.getExpirationConfiguration(thread)?.expiryMode?.coerceSendToRead(coerceDisappearAfterSendToRead) ?: ExpiryMode.NONE
|
||||
return this
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.session.libsession.messaging.messages.control
|
||||
|
||||
import org.session.libsession.messaging.messages.applyExpiryMode
|
||||
import org.session.libsession.messaging.messages.copyExpiration
|
||||
import org.session.libsignal.protos.SignalServiceProtos
|
||||
import org.session.libsignal.protos.SignalServiceProtos.CallMessage.Type.*
|
||||
@ -13,6 +14,7 @@ class CallMessage(): ControlMessage() {
|
||||
var sdpMids: List<String> = listOf()
|
||||
var callId: UUID? = null
|
||||
|
||||
override val coerceDisappearAfterSendToRead = true
|
||||
override val isSelfSendValid: Boolean get() = type in arrayOf(ANSWER, END_CALL)
|
||||
|
||||
override val defaultTtl: Long = 300000L // 5m
|
||||
@ -40,21 +42,21 @@ class CallMessage(): ControlMessage() {
|
||||
listOf(),
|
||||
listOf(),
|
||||
callId
|
||||
)
|
||||
).applyExpiryMode()
|
||||
|
||||
fun preOffer(callId: UUID) = CallMessage(PRE_OFFER,
|
||||
listOf(),
|
||||
listOf(),
|
||||
listOf(),
|
||||
callId
|
||||
)
|
||||
).applyExpiryMode()
|
||||
|
||||
fun offer(sdp: String, callId: UUID) = CallMessage(OFFER,
|
||||
listOf(sdp),
|
||||
listOf(),
|
||||
listOf(),
|
||||
callId
|
||||
)
|
||||
).applyExpiryMode()
|
||||
|
||||
fun endCall(callId: UUID) = CallMessage(END_CALL, emptyList(), emptyList(), emptyList(), callId)
|
||||
|
||||
@ -83,9 +85,8 @@ class CallMessage(): ControlMessage() {
|
||||
.addAllSdpMids(sdpMids)
|
||||
.setUuid(callId!!.toString())
|
||||
|
||||
val content = SignalServiceProtos.Content.newBuilder()
|
||||
content.setExpirationConfigurationIfNeeded(threadID, true)
|
||||
return content
|
||||
return SignalServiceProtos.Content.newBuilder()
|
||||
.applyExpiryMode()
|
||||
.setCallMessage(callMessage)
|
||||
.build()
|
||||
}
|
||||
|
@ -178,7 +178,7 @@ class ClosedGroupControlMessage() : ControlMessage() {
|
||||
contentProto.dataMessage = dataMessageProto.build()
|
||||
// Expiration timer
|
||||
val threadId = groupID?.let { MessagingModuleConfiguration.shared.storage.getOrCreateThreadIdFor(Address.fromSerialized(it)) }
|
||||
contentProto.setExpirationConfigurationIfNeeded(threadId)
|
||||
contentProto.applyExpiryMode()
|
||||
return contentProto.build()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Couldn't construct closed group control message proto from: $this.")
|
||||
|
@ -20,10 +20,10 @@ class ConfigurationMessage(var closedGroups: List<ClosedGroup>, var openGroups:
|
||||
|
||||
override val isSelfSendValid: Boolean = true
|
||||
|
||||
class ClosedGroup(var publicKey: String, var name: String, var encryptionKeyPair: ECKeyPair?, var members: List<String>, var admins: List<String>, var expirationTimer: Int) {
|
||||
class ClosedGroup(var publicKey: String, var name: String, var encryptionKeyPair: ECKeyPair?, var members: List<String>, var admins: List<String>) {
|
||||
val isValid: Boolean get() = members.isNotEmpty() && admins.isNotEmpty()
|
||||
|
||||
internal constructor() : this("", "", null, listOf(), listOf(), 0)
|
||||
internal constructor() : this("", "", null, listOf(), listOf())
|
||||
|
||||
override fun toString(): String {
|
||||
return name
|
||||
@ -40,8 +40,7 @@ class ConfigurationMessage(var closedGroups: List<ClosedGroup>, var openGroups:
|
||||
DjbECPrivateKey(encryptionKeyPairAsProto.privateKey.toByteArray()))
|
||||
val members = proto.membersList.map { it.toByteArray().toHexString() }
|
||||
val admins = proto.adminsList.map { it.toByteArray().toHexString() }
|
||||
val expirationTimer = proto.expirationTimer
|
||||
return ClosedGroup(publicKey, name, encryptionKeyPair, members, admins, expirationTimer)
|
||||
return ClosedGroup(publicKey, name, encryptionKeyPair, members, admins)
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,7 +54,6 @@ class ConfigurationMessage(var closedGroups: List<ClosedGroup>, var openGroups:
|
||||
result.encryptionKeyPair = encryptionKeyPairAsProto.build()
|
||||
result.addAllMembers(members.map { ByteString.copyFrom(Hex.fromStringCondensed(it)) })
|
||||
result.addAllAdmins(admins.map { ByteString.copyFrom(Hex.fromStringCondensed(it)) })
|
||||
result.expirationTimer = expirationTimer
|
||||
return result.build()
|
||||
}
|
||||
}
|
||||
@ -128,15 +126,12 @@ class ConfigurationMessage(var closedGroups: List<ClosedGroup>, var openGroups:
|
||||
if (!group.members.contains(Address.fromSerialized(storage.getUserPublicKey()!!))) continue
|
||||
val groupPublicKey = GroupUtil.doubleDecodeGroupID(group.encodedId).toHexString()
|
||||
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?.expiryMode?.expirySeconds?.toInt() ?: 0
|
||||
group.admins.map { it.serialize() }
|
||||
)
|
||||
closedGroups.add(closedGroup)
|
||||
}
|
||||
|
@ -7,6 +7,8 @@ import org.session.libsignal.utilities.Log
|
||||
class DataExtractionNotification() : ControlMessage() {
|
||||
var kind: Kind? = null
|
||||
|
||||
override val coerceDisappearAfterSendToRead = true
|
||||
|
||||
sealed class Kind {
|
||||
class Screenshot() : Kind()
|
||||
class MediaSaved(val timestamp: Long) : Kind()
|
||||
@ -64,10 +66,10 @@ class DataExtractionNotification() : ControlMessage() {
|
||||
dataExtractionNotification.timestamp = kind.timestamp
|
||||
}
|
||||
}
|
||||
val contentProto = SignalServiceProtos.Content.newBuilder()
|
||||
contentProto.dataExtractionNotification = dataExtractionNotification.build()
|
||||
contentProto.setExpirationConfigurationIfNeeded(threadID, true)
|
||||
return contentProto.build()
|
||||
return SignalServiceProtos.Content.newBuilder()
|
||||
.setDataExtractionNotification(dataExtractionNotification.build())
|
||||
.applyExpiryMode()
|
||||
.build()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Couldn't construct data extraction notification proto from: $this")
|
||||
return null
|
||||
|
@ -1,59 +1,53 @@
|
||||
package org.session.libsession.messaging.messages.control
|
||||
|
||||
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||
import org.session.libsession.messaging.messages.copyExpiration
|
||||
import org.session.libsession.messaging.messages.visible.VisibleMessage
|
||||
import org.session.libsignal.protos.SignalServiceProtos
|
||||
import org.session.libsignal.protos.SignalServiceProtos.DataMessage.Flags.EXPIRATION_TIMER_UPDATE_VALUE
|
||||
import org.session.libsignal.utilities.Log
|
||||
|
||||
/** In the case of a sync message, the public key of the person the message was targeted at.
|
||||
*
|
||||
* **Note:** `nil` if this isn't a sync message.
|
||||
*/
|
||||
data class ExpirationTimerUpdate(var syncTarget: String? = null) : ControlMessage() {
|
||||
data class ExpirationTimerUpdate(var syncTarget: String? = null, val isGroup: Boolean = false) : ControlMessage() {
|
||||
override val isSelfSendValid: Boolean = true
|
||||
|
||||
companion object {
|
||||
const val TAG = "ExpirationTimerUpdate"
|
||||
private val storage = MessagingModuleConfiguration.shared.storage
|
||||
|
||||
fun fromProto(proto: SignalServiceProtos.Content): ExpirationTimerUpdate? {
|
||||
val dataMessageProto = if (proto.hasDataMessage()) proto.dataMessage else return null
|
||||
val isExpirationTimerUpdate = dataMessageProto.flags.and(
|
||||
SignalServiceProtos.DataMessage.Flags.EXPIRATION_TIMER_UPDATE_VALUE
|
||||
) != 0
|
||||
if (!isExpirationTimerUpdate) return null
|
||||
|
||||
return ExpirationTimerUpdate(dataMessageProto.syncTarget)
|
||||
.copyExpiration(proto)
|
||||
}
|
||||
fun fromProto(proto: SignalServiceProtos.Content): ExpirationTimerUpdate? =
|
||||
proto.dataMessage?.takeIf { it.flags and EXPIRATION_TIMER_UPDATE_VALUE != 0 }?.run {
|
||||
ExpirationTimerUpdate(syncTarget, hasGroup()).copyExpiration(proto)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toProto(): SignalServiceProtos.Content? {
|
||||
val dataMessageProto = SignalServiceProtos.DataMessage.newBuilder()
|
||||
dataMessageProto.flags = SignalServiceProtos.DataMessage.Flags.EXPIRATION_TIMER_UPDATE_VALUE
|
||||
dataMessageProto.expireTimer = expiryMode.expirySeconds.toInt()
|
||||
// Sync target
|
||||
if (syncTarget != null) {
|
||||
dataMessageProto.syncTarget = syncTarget
|
||||
val dataMessageProto = SignalServiceProtos.DataMessage.newBuilder().apply {
|
||||
flags = EXPIRATION_TIMER_UPDATE_VALUE
|
||||
expireTimer = expiryMode.expirySeconds.toInt()
|
||||
}
|
||||
// Sync target
|
||||
syncTarget?.let { dataMessageProto.syncTarget = it }
|
||||
// Group context
|
||||
if (MessagingModuleConfiguration.shared.storage.isClosedGroup(recipient!!)) {
|
||||
if (storage.isClosedGroup(recipient!!)) {
|
||||
try {
|
||||
setGroupContext(dataMessageProto)
|
||||
} catch(e: Exception) {
|
||||
Log.w(VisibleMessage.TAG, "Couldn't construct visible message proto from: $this")
|
||||
Log.w(TAG, "Couldn't construct visible message proto from: $this", e)
|
||||
return null
|
||||
}
|
||||
}
|
||||
return try {
|
||||
SignalServiceProtos.Content.newBuilder().apply {
|
||||
dataMessage = dataMessageProto.build()
|
||||
setExpirationConfigurationIfNeeded(threadID)
|
||||
}.build()
|
||||
SignalServiceProtos.Content.newBuilder()
|
||||
.setDataMessage(dataMessageProto)
|
||||
.applyExpiryMode()
|
||||
.build()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Couldn't construct expiration timer update proto from: $this")
|
||||
Log.w(TAG, "Couldn't construct expiration timer update proto from: $this", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ class MessageRequestResponse(val isApproved: Boolean, var profile: Profile? = nu
|
||||
profile?.profileKey?.let { messageRequestResponseProto.profileKey = ByteString.copyFrom(it) }
|
||||
return try {
|
||||
SignalServiceProtos.Content.newBuilder()
|
||||
.setExpirationConfigurationIfNeeded(threadID)
|
||||
.applyExpiryMode()
|
||||
.setMessageRequestResponse(messageRequestResponseProto.build())
|
||||
.build()
|
||||
} catch (e: Exception) {
|
||||
|
@ -37,14 +37,15 @@ class ReadReceipt() : ControlMessage() {
|
||||
Log.w(TAG, "Couldn't construct read receipt proto from: $this")
|
||||
return null
|
||||
}
|
||||
val receiptProto = SignalServiceProtos.ReceiptMessage.newBuilder()
|
||||
receiptProto.type = SignalServiceProtos.ReceiptMessage.Type.READ
|
||||
receiptProto.addAllTimestamp(timestamps.asIterable())
|
||||
val contentProto = SignalServiceProtos.Content.newBuilder()
|
||||
|
||||
return try {
|
||||
contentProto.receiptMessage = receiptProto.build()
|
||||
contentProto.setExpirationConfigurationIfNeeded(threadID)
|
||||
contentProto.build()
|
||||
SignalServiceProtos.Content.newBuilder()
|
||||
.setReceiptMessage(
|
||||
SignalServiceProtos.ReceiptMessage.newBuilder()
|
||||
.setType(SignalServiceProtos.ReceiptMessage.Type.READ)
|
||||
.addAllTimestamp(timestamps.asIterable()).build()
|
||||
).applyExpiryMode()
|
||||
.build()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Couldn't construct read receipt proto from: $this")
|
||||
null
|
||||
|
@ -56,14 +56,11 @@ class TypingIndicator() : ControlMessage() {
|
||||
Log.w(TAG, "Couldn't construct typing indicator proto from: $this")
|
||||
return null
|
||||
}
|
||||
val typingIndicatorProto = SignalServiceProtos.TypingMessage.newBuilder()
|
||||
typingIndicatorProto.timestamp = timestamp
|
||||
typingIndicatorProto.action = kind.toProto()
|
||||
val contentProto = SignalServiceProtos.Content.newBuilder()
|
||||
return try {
|
||||
contentProto.typingMessage = typingIndicatorProto.build()
|
||||
contentProto.setExpirationConfigurationIfNeeded(threadID)
|
||||
contentProto.build()
|
||||
SignalServiceProtos.Content.newBuilder()
|
||||
.setTypingMessage(SignalServiceProtos.TypingMessage.newBuilder().setTimestamp(timestamp).setAction(kind.toProto()).build())
|
||||
.applyExpiryMode()
|
||||
.build()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Couldn't construct typing indicator proto from: $this")
|
||||
null
|
||||
|
@ -41,14 +41,11 @@ class UnsendRequest(): ControlMessage() {
|
||||
Log.w(TAG, "Couldn't construct unsend request proto from: $this")
|
||||
return null
|
||||
}
|
||||
val unsendRequestProto = SignalServiceProtos.UnsendRequest.newBuilder()
|
||||
unsendRequestProto.timestamp = timestamp
|
||||
unsendRequestProto.author = author
|
||||
val contentProto = SignalServiceProtos.Content.newBuilder()
|
||||
return try {
|
||||
contentProto.unsendRequest = unsendRequestProto.build()
|
||||
contentProto.setExpirationConfigurationIfNeeded(threadID)
|
||||
contentProto.build()
|
||||
SignalServiceProtos.Content.newBuilder()
|
||||
.setUnsendRequest(SignalServiceProtos.UnsendRequest.newBuilder().setTimestamp(timestamp).setAuthor(author).build())
|
||||
.applyExpiryMode()
|
||||
.build()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Couldn't construct unsend request proto from: $this")
|
||||
null
|
||||
|
@ -44,52 +44,26 @@ data class VisibleMessage(
|
||||
companion object {
|
||||
const val TAG = "VisibleMessage"
|
||||
|
||||
fun fromProto(proto: SignalServiceProtos.Content): VisibleMessage? {
|
||||
val dataMessage = proto.dataMessage ?: return null
|
||||
val result = VisibleMessage()
|
||||
if (dataMessage.hasSyncTarget()) { result.syncTarget = dataMessage.syncTarget }
|
||||
result.text = dataMessage.body
|
||||
// Attachments are handled in MessageReceiver
|
||||
val quoteProto = if (dataMessage.hasQuote()) dataMessage.quote else null
|
||||
if (quoteProto != null) {
|
||||
val quote = Quote.fromProto(quoteProto)
|
||||
result.quote = quote
|
||||
}
|
||||
val linkPreviewProto = dataMessage.previewList.firstOrNull()
|
||||
if (linkPreviewProto != null) {
|
||||
val linkPreview = LinkPreview.fromProto(linkPreviewProto)
|
||||
result.linkPreview = linkPreview
|
||||
}
|
||||
val openGroupInvitationProto = if (dataMessage.hasOpenGroupInvitation()) dataMessage.openGroupInvitation else null
|
||||
if (openGroupInvitationProto != null) {
|
||||
val openGroupInvitation = OpenGroupInvitation.fromProto(openGroupInvitationProto)
|
||||
result.openGroupInvitation = openGroupInvitation
|
||||
}
|
||||
// TODO Contact
|
||||
val profile = Profile.fromProto(dataMessage)
|
||||
if (profile != null) { result.profile = profile }
|
||||
val reactionProto = if (dataMessage.hasReaction()) dataMessage.reaction else null
|
||||
if (reactionProto != null) {
|
||||
val reaction = Reaction.fromProto(reactionProto)
|
||||
result.reaction = reaction
|
||||
}
|
||||
|
||||
result.blocksMessageRequests = with (dataMessage) { hasBlocksCommunityMessageRequests() && blocksCommunityMessageRequests }
|
||||
|
||||
return result.copyExpiration(proto)
|
||||
fun fromProto(proto: SignalServiceProtos.Content): VisibleMessage? =
|
||||
proto.dataMessage?.let { VisibleMessage().apply {
|
||||
if (it.hasSyncTarget()) syncTarget = it.syncTarget
|
||||
text = it.body
|
||||
// Attachments are handled in MessageReceiver
|
||||
if (it.hasQuote()) quote = Quote.fromProto(it.quote)
|
||||
linkPreview = it.previewList.firstOrNull()?.let(LinkPreview::fromProto)
|
||||
if (it.hasOpenGroupInvitation()) openGroupInvitation = it.openGroupInvitation?.let(OpenGroupInvitation::fromProto)
|
||||
// TODO Contact
|
||||
profile = Profile.fromProto(it)
|
||||
if (it.hasReaction()) reaction = it.reaction?.let(Reaction::fromProto)
|
||||
blocksMessageRequests = it.hasBlocksCommunityMessageRequests() && it.blocksCommunityMessageRequests
|
||||
}.copyExpiration(proto)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toProto(): SignalServiceProtos.Content? {
|
||||
val proto = SignalServiceProtos.Content.newBuilder()
|
||||
val dataMessage: SignalServiceProtos.DataMessage.Builder
|
||||
// Profile
|
||||
val profileProto = profile?.toProto()
|
||||
dataMessage = if (profileProto != null) {
|
||||
profileProto.toBuilder()
|
||||
} else {
|
||||
SignalServiceProtos.DataMessage.newBuilder()
|
||||
}
|
||||
val dataMessage = profile?.toProto()?.toBuilder() ?: SignalServiceProtos.DataMessage.newBuilder()
|
||||
// Text
|
||||
if (text != null) { dataMessage.body = text }
|
||||
// Quote
|
||||
@ -124,7 +98,7 @@ data class VisibleMessage(
|
||||
dataMessage.addAllAttachments(pointers)
|
||||
// TODO: Contact
|
||||
// Expiration timer
|
||||
proto.setExpirationConfigurationIfNeeded(threadID)
|
||||
proto.applyExpiryMode()
|
||||
// Group context
|
||||
val storage = MessagingModuleConfiguration.shared.storage
|
||||
if (storage.isClosedGroup(recipient!!)) {
|
||||
@ -163,4 +137,4 @@ data class VisibleMessage(
|
||||
fun isMediaMessage(): Boolean {
|
||||
return attachmentIDs.isNotEmpty() || quote != null || linkPreview != null || reaction != null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -145,9 +145,7 @@ object MessageReceiver {
|
||||
MessageRequestResponse.fromProto(proto) ?:
|
||||
CallMessage.fromProto(proto) ?:
|
||||
SharedConfigurationMessage.fromProto(proto) ?:
|
||||
VisibleMessage.fromProto(proto) ?: run {
|
||||
throw Error.UnknownMessage
|
||||
}
|
||||
VisibleMessage.fromProto(proto) ?: throw Error.UnknownMessage
|
||||
|
||||
val isUserBlindedSender = sender == openGroupPublicKey?.let { SodiumUtilities.blindedKeyPair(it, MessagingModuleConfiguration.shared.getUserED25519KeyPair()!!) }?.let { SessionId(IdPrefix.BLINDED, it.publicKey.asBytes).hexString }
|
||||
val isUserSender = sender == userPublicKey
|
||||
|
@ -9,6 +9,7 @@ import org.session.libsession.messaging.jobs.MessageSendJob
|
||||
import org.session.libsession.messaging.jobs.NotifyPNServerJob
|
||||
import org.session.libsession.messaging.messages.Destination
|
||||
import org.session.libsession.messaging.messages.Message
|
||||
import org.session.libsession.messaging.messages.applyExpiryMode
|
||||
import org.session.libsession.messaging.messages.control.CallMessage
|
||||
import org.session.libsession.messaging.messages.control.ClosedGroupControlMessage
|
||||
import org.session.libsession.messaging.messages.control.ConfigurationMessage
|
||||
@ -421,12 +422,7 @@ object MessageSender {
|
||||
storage.markUnidentified(timestamp, userPublicKey)
|
||||
// Start the disappearing messages timer if needed
|
||||
Log.d("MessageSender", "Start the disappearing messages timer if needed message.recipient = ${message.recipient}, userPublicKey = $userPublicKey, isSyncMessage = $isSyncMessage")
|
||||
message.threadID?.let(storage::getExpirationConfiguration)?.expiryMode?.takeIf { it.expirySeconds > 0 }?.let { mode ->
|
||||
if (message.recipient == userPublicKey || !isSyncMessage) {
|
||||
val expireStartedAt = if (mode is ExpiryMode.AfterRead) timestamp + 1 else timestamp
|
||||
SSKEnvironment.shared.messageExpirationManager.startAnyExpiration(timestamp, userPublicKey, expireStartedAt)
|
||||
}
|
||||
}
|
||||
SSKEnvironment.shared.messageExpirationManager.maybeStartExpiration(message, startDisappearAfterRead = true)
|
||||
} ?: run {
|
||||
storage.updateReactionIfNeeded(message, message.sender?:userPublicKey, openGroupSentTimestamp)
|
||||
}
|
||||
@ -475,6 +471,7 @@ object MessageSender {
|
||||
|
||||
@JvmStatic
|
||||
fun send(message: Message, address: Address) {
|
||||
message.applyExpiryMode()
|
||||
val threadID = MessagingModuleConfiguration.shared.storage.getThreadId(address)
|
||||
message.threadID = threadID
|
||||
val destination = Destination.from(address)
|
||||
|
@ -124,7 +124,6 @@ private fun MessageReceiver.handleReadReceipt(message: ReadReceipt) {
|
||||
private fun MessageReceiver.handleCallMessage(message: CallMessage) {
|
||||
// TODO: refactor this out to persistence, just to help debug the flow and send/receive in synchronous testing
|
||||
WebRtcUtils.SIGNAL_QUEUE.trySend(message)
|
||||
SSKEnvironment.shared.messageExpirationManager.startAnyExpiration(message, coerceToDisappearAfterRead = true)
|
||||
}
|
||||
|
||||
private fun MessageReceiver.handleTypingIndicator(message: TypingIndicator) {
|
||||
@ -192,7 +191,6 @@ private fun MessageReceiver.handleDataExtractionNotification(message: DataExtrac
|
||||
else -> return
|
||||
}
|
||||
storage.insertDataExtractionNotificationMessage(senderPublicKey, notification, message.sentTimestamp!!)
|
||||
SSKEnvironment.shared.messageExpirationManager.startAnyExpiration(message, coerceToDisappearAfterRead = true)
|
||||
}
|
||||
|
||||
private fun handleConfigurationMessage(message: ConfigurationMessage) {
|
||||
@ -221,7 +219,7 @@ private fun handleConfigurationMessage(message: ConfigurationMessage) {
|
||||
} else {
|
||||
// only handle new closed group if it's first time sync
|
||||
handleNewClosedGroup(message.sender!!, message.sentTimestamp!!, closedGroup.publicKey, closedGroup.name,
|
||||
closedGroup.encryptionKeyPair!!, closedGroup.members, closedGroup.admins, message.sentTimestamp!!, closedGroup.expirationTimer)
|
||||
closedGroup.encryptionKeyPair!!, closedGroup.members, closedGroup.admins, message.sentTimestamp!!)
|
||||
}
|
||||
}
|
||||
val allV2OpenGroups = storage.getAllOpenGroups().map { it.value.joinURL }
|
||||
@ -431,7 +429,7 @@ fun MessageReceiver.handleVisibleMessage(
|
||||
val isSms = !message.isMediaMessage() && attachments.isEmpty()
|
||||
storage.setOpenGroupServerMessageID(messageID, it, threadID, isSms)
|
||||
}
|
||||
SSKEnvironment.shared.messageExpirationManager.startAnyExpiration(message)
|
||||
SSKEnvironment.shared.messageExpirationManager.maybeStartExpiration(message)
|
||||
return messageID
|
||||
}
|
||||
return null
|
||||
@ -550,10 +548,10 @@ private fun MessageReceiver.handleNewClosedGroup(message: ClosedGroupControlMess
|
||||
val members = kind.members.map { it.toByteArray().toHexString() }
|
||||
val admins = kind.admins.map { it.toByteArray().toHexString() }
|
||||
val expirationTimer = kind.expirationTimer
|
||||
handleNewClosedGroup(message.sender!!, message.sentTimestamp!!, groupPublicKey, kind.name, kind.encryptionKeyPair!!, members, admins, message.sentTimestamp!!, expirationTimer)
|
||||
handleNewClosedGroup(message.sender!!, message.sentTimestamp!!, groupPublicKey, kind.name, kind.encryptionKeyPair!!, members, admins, message.sentTimestamp!!)
|
||||
}
|
||||
|
||||
private fun handleNewClosedGroup(sender: String, sentTimestamp: Long, groupPublicKey: String, name: String, encryptionKeyPair: ECKeyPair, members: List<String>, admins: List<String>, formationTimestamp: Long, expireTimer: Int) {
|
||||
private fun handleNewClosedGroup(sender: String, sentTimestamp: Long, groupPublicKey: String, name: String, encryptionKeyPair: ECKeyPair, members: List<String>, admins: List<String>, formationTimestamp: Long) {
|
||||
val context = MessagingModuleConfiguration.shared.context
|
||||
val storage = MessagingModuleConfiguration.shared.storage
|
||||
val userPublicKey = storage.getUserPublicKey()!!
|
||||
|
@ -3,11 +3,12 @@ package org.session.libsession.utilities
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||
import org.session.libsession.messaging.contacts.Contact
|
||||
import org.session.libsession.messaging.messages.Message
|
||||
import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate
|
||||
import org.session.libsession.messaging.sending_receiving.notifications.MessageNotifier
|
||||
import org.session.libsession.snode.SnodeAPI
|
||||
import org.session.libsession.snode.SnodeAPI.nowWithOffset
|
||||
import org.session.libsession.utilities.recipients.Recipient
|
||||
|
||||
class SSKEnvironment(
|
||||
@ -43,18 +44,41 @@ class SSKEnvironment(
|
||||
interface MessageExpirationManagerProtocol {
|
||||
fun insertExpirationTimerMessage(message: ExpirationTimerUpdate)
|
||||
fun startAnyExpiration(timestamp: Long, author: String, expireStartedAt: Long)
|
||||
fun startAnyExpiration(message: Message, coerceToDisappearAfterRead: Boolean = false) {
|
||||
Log.d("MessageExpirationManagerProtocol", "startAnyExpiration() called with: message = $message, coerceToDisappearAfterRead = $coerceToDisappearAfterRead")
|
||||
|
||||
val timestamp = message.sentTimestamp ?: return
|
||||
startAnyExpiration(
|
||||
timestamp = timestamp,
|
||||
author = message.sender ?: return,
|
||||
expireStartedAt = if (message.expiryMode is ExpiryMode.AfterRead || coerceToDisappearAfterRead && message.expiryMode.expiryMillis > 0) SnodeAPI.nowWithOffset.coerceAtLeast(timestamp + 1)
|
||||
else if (message.expiryMode is ExpiryMode.AfterSend) timestamp
|
||||
else return
|
||||
fun maybeStartExpiration(message: Message, startDisappearAfterRead: Boolean = false) {
|
||||
Log.d("MessageExpirationManagerProtocol", "maybeStartExpiration() called with: message = $message, startDisappearAfterRead = $startDisappearAfterRead")
|
||||
|
||||
if (message is ExpirationTimerUpdate && message.isGroup) return
|
||||
|
||||
maybeStartExpiration(
|
||||
message.sentTimestamp ?: return,
|
||||
message.sender ?: return,
|
||||
message.expiryMode,
|
||||
startDisappearAfterRead || message.isSenderSelf
|
||||
)
|
||||
}
|
||||
|
||||
fun startDisappearAfterRead(timestamp: Long, sender: String) {
|
||||
Log.d("MessageExpirationManagerProtocol", "startDisappearAfterRead() called with: timestamp = $timestamp, sender = $sender")
|
||||
|
||||
startAnyExpiration(
|
||||
timestamp,
|
||||
sender,
|
||||
expireStartedAt = nowWithOffset.coerceAtLeast(timestamp + 1)
|
||||
)
|
||||
}
|
||||
|
||||
fun maybeStartExpiration(timestamp: Long, sender: String, mode: ExpiryMode, startDisappearAfterRead: Boolean = false) {
|
||||
Log.d("MessageExpirationManagerProtocol", "maybeStartExpiration() called with: timestamp = $timestamp, sender = $sender, mode = $mode, startDisappearAfterRead = $startDisappearAfterRead")
|
||||
|
||||
val expireStartedAt = when (mode) {
|
||||
is ExpiryMode.AfterSend -> timestamp
|
||||
is ExpiryMode.AfterRead -> if (startDisappearAfterRead) nowWithOffset.coerceAtLeast(timestamp + 1) else return
|
||||
else -> return
|
||||
}
|
||||
|
||||
startAnyExpiration(timestamp, sender, expireStartedAt)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
Loading…
Reference in New Issue
Block a user