From 9adcd3eafb17375d49ec302c0ac8f9df75356e63 Mon Sep 17 00:00:00 2001 From: SessionHero01 <180888785+SessionHero01@users.noreply.github.com> Date: Wed, 26 Nov 2025 17:02:34 +1100 Subject: [PATCH 1/3] WIP --- .../messages/ProfileUpdateHandler.kt | 48 +++++++++++++++++-- .../messages/signal/IncomingMediaMessage.kt | 5 +- .../messages/signal/IncomingTextMessage.kt | 9 ++-- .../messages/signal/OutgoingMediaMessage.kt | 5 +- .../messages/signal/OutgoingTextMessage.kt | 5 +- .../messages/visible/VisibleMessage.kt | 9 ++-- .../sending_receiving/MessageParser.kt | 1 - .../MessageRequestResponseHandler.kt | 4 +- .../utilities/TextSecurePreferences.kt | 13 ++--- .../conversation/v2/ConversationViewModel.kt | 4 +- .../conversation/v2/MessageDetailActivity.kt | 16 ++++--- .../v2/MessageDetailsViewModel.kt | 9 ++-- .../securesms/database/RecipientRepository.kt | 8 ++-- .../securesms/database/Storage.kt | 8 ++-- .../database/model/MessageRecords.kt | 5 +- .../database/model/ReactionRecord.kt | 6 +++ .../database/model/RecipientSettings.kt | 13 +++++ .../securesms/debugmenu/DebugMenu.kt | 8 ++-- .../securesms/debugmenu/DebugMenuViewModel.kt | 14 +++--- .../securesms/pro/ProStatusManager.kt | 5 +- .../service/ExpiringMessageManager.kt | 4 +- 21 files changed, 136 insertions(+), 63 deletions(-) diff --git a/app/src/main/java/org/session/libsession/messaging/messages/ProfileUpdateHandler.kt b/app/src/main/java/org/session/libsession/messaging/messages/ProfileUpdateHandler.kt index a377e9fc65..86a6b8ec96 100644 --- a/app/src/main/java/org/session/libsession/messaging/messages/ProfileUpdateHandler.kt +++ b/app/src/main/java/org/session/libsession/messaging/messages/ProfileUpdateHandler.kt @@ -1,7 +1,10 @@ package org.session.libsession.messaging.messages import com.google.protobuf.ByteString +import network.loki.messenger.libsession_util.protocol.ProProfileFeature import network.loki.messenger.libsession_util.util.BaseCommunityInfo +import network.loki.messenger.libsession_util.util.BitSet +import network.loki.messenger.libsession_util.util.Conversation import network.loki.messenger.libsession_util.util.UserPic import org.session.libsession.utilities.Address import org.session.libsession.utilities.Address.Companion.toAddress @@ -13,6 +16,7 @@ import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.database.BlindMappingRepository import org.thoughtcrime.securesms.database.RecipientRepository import org.thoughtcrime.securesms.database.RecipientSettingsDatabase +import org.thoughtcrime.securesms.database.model.RecipientSettings import org.thoughtcrime.securesms.util.DateUtils.Companion.secondsToInstant import org.thoughtcrime.securesms.util.DateUtils.Companion.toEpochSeconds import java.time.Instant @@ -57,11 +61,14 @@ class ProfileUpdateHandler @Inject constructor( val standardSender = unblinded ?: (senderAddress as? Address.Standard) if (standardSender != null && (!updates.name.isNullOrBlank() || updates.pic != null)) { configFactory.withMutableUserConfigs { configs -> + var shouldUpdate = false configs.contacts.updateContact(standardSender) { - if (shouldUpdateProfile( + shouldUpdate = shouldUpdateProfile( lastUpdated = profileUpdatedEpochSeconds.secondsToInstant(), newUpdateTime = updates.profileUpdateTime - )) { + ) + + if (shouldUpdate) { if (updates.name != null) { name = updates.name } @@ -70,6 +77,10 @@ class ProfileUpdateHandler @Inject constructor( profilePicture = updates.pic } + if (updates.proFeatures != null) { + proFeatures = updates.proFeatures + } + if (updates.profileUpdateTime != null) { profileUpdatedEpochSeconds = updates.profileUpdateTime.toEpochSeconds() } @@ -78,13 +89,20 @@ class ProfileUpdateHandler @Inject constructor( Log.d(TAG, "Ignoring contact profile update for ${standardSender.debugString}, no changes detected") } } + + if (shouldUpdate) { + configs.convoInfoVolatile.set( + configs.convoInfoVolatile.getOrConstructOneToOne(standardSender.accountId.hexString) + .copy(proProofInfo = updates.proProof) + ) + } } } // If we have a blinded address, we need to look at if we have a blinded contact to update if (senderAddress is Address.Blinded && (updates.pic != null || !updates.name.isNullOrBlank())) { configFactory.withMutableUserConfigs { configs -> - configs.contacts.getBlinded(senderAddress.blindedId.hexString)?.let { c -> + val shouldUpdate = configs.contacts.getBlinded(senderAddress.blindedId.hexString)?.let { c -> if (shouldUpdateProfile( lastUpdated = c.profileUpdatedEpochSeconds.secondsToInstant(), newUpdateTime = updates.profileUpdateTime @@ -97,12 +115,26 @@ class ProfileUpdateHandler @Inject constructor( c.name = updates.name } + if (updates.proFeatures != null) { + c.proFeatures = updates.proFeatures + } + if (updates.profileUpdateTime != null) { c.profileUpdatedEpochSeconds = updates.profileUpdateTime.toEpochSeconds() } configs.contacts.setBlinded(c) + true + } else { + false } + } == true + + if (shouldUpdate) { + configs.convoInfoVolatile.set( + configs.convoInfoVolatile.getOrConstructedBlindedOneToOne(senderAddress.blindedId.hexString) + .copy(proProofInfo = updates.proProof) + ) } } } @@ -122,7 +154,13 @@ class ProfileUpdateHandler @Inject constructor( r.copy( name = updates.name ?: r.name, profilePic = updates.pic ?: r.profilePic, - blocksCommunityMessagesRequests = updates.blocksCommunityMessageRequests ?: r.blocksCommunityMessagesRequests + blocksCommunityMessagesRequests = updates.blocksCommunityMessageRequests ?: r.blocksCommunityMessagesRequests, + proData = updates.proProof?.let { + RecipientSettings.ProData( + info = it, + features = updates.proFeatures ?: BitSet() + ) + }, ) } else if (updates.blocksCommunityMessageRequests != null && r.blocksCommunityMessagesRequests != updates.blocksCommunityMessageRequests) { @@ -155,6 +193,8 @@ class ProfileUpdateHandler @Inject constructor( // Name to update, must be non-blank if provided. val name: String? = null, val pic: UserPic? = null, + val proProof: Conversation.ProProofInfo? = null, + val proFeatures: BitSet? = null, val blocksCommunityMessageRequests: Boolean? = null, val profileUpdateTime: Instant?, ) { diff --git a/app/src/main/java/org/session/libsession/messaging/messages/signal/IncomingMediaMessage.kt b/app/src/main/java/org/session/libsession/messaging/messages/signal/IncomingMediaMessage.kt index 727cdbbe42..4c9a9482a9 100644 --- a/app/src/main/java/org/session/libsession/messaging/messages/signal/IncomingMediaMessage.kt +++ b/app/src/main/java/org/session/libsession/messaging/messages/signal/IncomingMediaMessage.kt @@ -1,6 +1,7 @@ package org.session.libsession.messaging.messages.signal -import network.loki.messenger.libsession_util.protocol.ProFeatures +import network.loki.messenger.libsession_util.protocol.ProMessageFeature +import network.loki.messenger.libsession_util.util.BitSet import org.session.libsession.messaging.messages.visible.VisibleMessage import org.session.libsession.messaging.sending_receiving.attachments.Attachment import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage @@ -19,7 +20,7 @@ class IncomingMediaMessage( val body: String?, val group: Address.GroupLike?, val attachments: List, - val proFeatures: ProFeatures, + val proFeatures: BitSet, val messageContent: MessageContent?, val quote: QuoteModel?, val linkPreviews: List, diff --git a/app/src/main/java/org/session/libsession/messaging/messages/signal/IncomingTextMessage.kt b/app/src/main/java/org/session/libsession/messaging/messages/signal/IncomingTextMessage.kt index a229663295..ac619f5f89 100644 --- a/app/src/main/java/org/session/libsession/messaging/messages/signal/IncomingTextMessage.kt +++ b/app/src/main/java/org/session/libsession/messaging/messages/signal/IncomingTextMessage.kt @@ -1,6 +1,7 @@ package org.session.libsession.messaging.messages.signal -import network.loki.messenger.libsession_util.protocol.ProFeatures +import network.loki.messenger.libsession_util.protocol.ProMessageFeature +import network.loki.messenger.libsession_util.util.BitSet import org.session.libsession.messaging.calls.CallMessageType import org.session.libsession.messaging.messages.visible.OpenGroupInvitation import org.session.libsession.messaging.messages.visible.VisibleMessage @@ -20,7 +21,7 @@ data class IncomingTextMessage( val hasMention: Boolean, val isOpenGroupInvitation: Boolean, val isSecureMessage: Boolean, - val proFeatures: ProFeatures, + val proFeatures: BitSet, val isGroupMessage: Boolean = false, val isGroupUpdateMessage: Boolean = false, ) { @@ -80,7 +81,7 @@ data class IncomingTextMessage( hasMention = false, isOpenGroupInvitation = false, isSecureMessage = false, - proFeatures = ProFeatures.NONE, + proFeatures = BitSet(), ) companion object { @@ -108,7 +109,7 @@ data class IncomingTextMessage( hasMention = false, isOpenGroupInvitation = true, isSecureMessage = false, - proFeatures = ProFeatures.NONE, + proFeatures = BitSet(), ) } } diff --git a/app/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingMediaMessage.kt b/app/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingMediaMessage.kt index c304453493..fcc138998c 100644 --- a/app/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingMediaMessage.kt +++ b/app/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingMediaMessage.kt @@ -1,6 +1,7 @@ package org.session.libsession.messaging.messages.signal -import network.loki.messenger.libsession_util.protocol.ProFeatures +import network.loki.messenger.libsession_util.protocol.ProMessageFeature +import network.loki.messenger.libsession_util.util.BitSet import org.session.libsession.messaging.messages.visible.VisibleMessage import org.session.libsession.messaging.sending_receiving.attachments.Attachment import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview @@ -20,7 +21,7 @@ class OutgoingMediaMessage( val linkPreviews: List, val group: Address.GroupLike?, val isGroupUpdateMessage: Boolean, - val proFeatures: ProFeatures = ProFeatures.NONE, + val proFeatures: BitSet = BitSet() ) { init { check(!isGroupUpdateMessage || group != null) { diff --git a/app/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingTextMessage.kt b/app/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingTextMessage.kt index 56e24b1a5a..c4cc2d847a 100644 --- a/app/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingTextMessage.kt +++ b/app/src/main/java/org/session/libsession/messaging/messages/signal/OutgoingTextMessage.kt @@ -1,6 +1,7 @@ package org.session.libsession.messaging.messages.signal -import network.loki.messenger.libsession_util.protocol.ProFeatures +import network.loki.messenger.libsession_util.protocol.ProMessageFeature +import network.loki.messenger.libsession_util.util.BitSet import org.session.libsession.messaging.messages.visible.OpenGroupInvitation import org.session.libsession.messaging.messages.visible.VisibleMessage import org.session.libsession.messaging.utilities.UpdateMessageData @@ -13,7 +14,7 @@ data class OutgoingTextMessage( val expireStartedAtMillis: Long, val sentTimestampMillis: Long, val isOpenGroupInvitation: Boolean, - val proFeatures: ProFeatures = ProFeatures.NONE, + val proFeatures: BitSet = BitSet() ) { constructor( message: VisibleMessage, diff --git a/app/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessage.kt b/app/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessage.kt index 5a0f65b108..968ffda0b8 100644 --- a/app/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessage.kt +++ b/app/src/main/java/org/session/libsession/messaging/messages/visible/VisibleMessage.kt @@ -2,7 +2,8 @@ package org.session.libsession.messaging.messages.visible import androidx.annotation.Keep import network.loki.messenger.BuildConfig -import network.loki.messenger.libsession_util.protocol.ProFeatures +import network.loki.messenger.libsession_util.protocol.ProMessageFeature +import network.loki.messenger.libsession_util.util.BitSet import org.session.libsession.database.MessageDataProvider import org.session.libsession.messaging.messages.Message import org.session.libsession.messaging.messages.copyExpiration @@ -26,12 +27,12 @@ data class VisibleMessage( var reaction: Reaction? = null, var hasMention: Boolean = false, var blocksMessageRequests: Boolean = false, - var proFeatures: ProFeatures = ProFeatures.NONE + var proFeatures: BitSet = BitSet() ) : Message() { // This empty constructor is needed for kryo serialization @Keep - constructor(): this(proFeatures = ProFeatures.NONE) + constructor(): this(proFeatures = BitSet()) override val isSelfSendValid: Boolean = true @@ -113,7 +114,7 @@ data class VisibleMessage( } // Pro features - if (proFeatures != ProFeatures.NONE) { + if (!proFeatures.isEmpty) { builder.proMessageBuilder.setFeatures(proFeatures.rawValue) } } diff --git a/app/src/main/java/org/session/libsession/messaging/sending_receiving/MessageParser.kt b/app/src/main/java/org/session/libsession/messaging/sending_receiving/MessageParser.kt index eb12a41092..c465f9059b 100644 --- a/app/src/main/java/org/session/libsession/messaging/sending_receiving/MessageParser.kt +++ b/app/src/main/java/org/session/libsession/messaging/sending_receiving/MessageParser.kt @@ -4,7 +4,6 @@ import network.loki.messenger.libsession_util.ED25519 import network.loki.messenger.libsession_util.SessionEncrypt import network.loki.messenger.libsession_util.pro.ProProof import network.loki.messenger.libsession_util.protocol.DecodedEnvelope -import network.loki.messenger.libsession_util.protocol.ProFeatures import network.loki.messenger.libsession_util.protocol.SessionProtocol import org.session.libsession.database.StorageProtocol import org.session.libsession.messaging.messages.Message diff --git a/app/src/main/java/org/session/libsession/messaging/sending_receiving/MessageRequestResponseHandler.kt b/app/src/main/java/org/session/libsession/messaging/sending_receiving/MessageRequestResponseHandler.kt index 8a4976bac0..1764ba1817 100644 --- a/app/src/main/java/org/session/libsession/messaging/sending_receiving/MessageRequestResponseHandler.kt +++ b/app/src/main/java/org/session/libsession/messaging/sending_receiving/MessageRequestResponseHandler.kt @@ -1,6 +1,6 @@ package org.session.libsession.messaging.sending_receiving -import network.loki.messenger.libsession_util.protocol.ProFeatures +import network.loki.messenger.libsession_util.util.BitSet import org.session.libsession.messaging.messages.Message import org.session.libsession.messaging.messages.ProfileUpdateHandler import org.session.libsession.messaging.messages.control.MessageRequestResponse @@ -161,7 +161,7 @@ class MessageRequestResponseHandler @Inject constructor( body = null, group = null, attachments = emptyList(), - proFeatures = ProFeatures.NONE, + proFeatures = BitSet(), messageContent = null, quote = null, linkPreviews = emptyList(), diff --git a/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt b/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt index 2d88fe51a1..7670926387 100644 --- a/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt +++ b/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt @@ -20,7 +20,8 @@ import kotlinx.coroutines.flow.update import kotlinx.serialization.json.Json import network.loki.messenger.BuildConfig import network.loki.messenger.R -import network.loki.messenger.libsession_util.protocol.ProFeatures +import network.loki.messenger.libsession_util.protocol.ProMessageFeature +import network.loki.messenger.libsession_util.util.BitSet import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.file_server.FileServer import org.session.libsession.utilities.TextSecurePreferences.Companion.AUTOPLAY_AUDIO_MESSAGES @@ -215,8 +216,8 @@ interface TextSecurePreferences { fun forcedShortTTL(): Boolean fun setForcedShortTTL(value: Boolean) - fun getDebugMessageFeatures(): ProFeatures - fun setDebugMessageFeatures(features: ProFeatures) + fun getDebugMessageFeatures(): BitSet + fun setDebugMessageFeatures(features: BitSet) fun getDebugSubscriptionType(): DebugMenuViewModel.DebugSubscriptionStatus? fun setDebugSubscriptionType(status: DebugMenuViewModel.DebugSubscriptionStatus?) @@ -1750,11 +1751,11 @@ class AppTextSecurePreferences @Inject constructor( setStringPreference(TextSecurePreferences.DEPRECATING_START_TIME_OVERRIDE, value.toString()) } } - override fun getDebugMessageFeatures(): ProFeatures { - return ProFeatures(getLongPreference( TextSecurePreferences.DEBUG_MESSAGE_FEATURES, 0)) + override fun getDebugMessageFeatures(): BitSet { + return BitSet(getLongPreference( TextSecurePreferences.DEBUG_MESSAGE_FEATURES, 0)) } - override fun setDebugMessageFeatures(features: ProFeatures) { + override fun setDebugMessageFeatures(features: BitSet) { setLongPreference(TextSecurePreferences.DEBUG_MESSAGE_FEATURES, features.rawValue) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt index 0b76d7c597..f44375d22c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationViewModel.kt @@ -46,7 +46,7 @@ import kotlinx.coroutines.withContext import network.loki.messenger.R import network.loki.messenger.libsession_util.PRIORITY_HIDDEN import network.loki.messenger.libsession_util.PRIORITY_VISIBLE -import network.loki.messenger.libsession_util.protocol.ProFeatures +import network.loki.messenger.libsession_util.util.BitSet import network.loki.messenger.libsession_util.util.BlindKeyAPI import network.loki.messenger.libsession_util.util.BlindedContact import network.loki.messenger.libsession_util.util.ExpiryMode @@ -1383,7 +1383,7 @@ class ConversationViewModel @AssistedInject constructor( profilePic = recipient.data.avatar?.toUserPic() ?: UserPic.DEFAULT, profileUpdatedEpochSeconds = recipient.data.profileUpdatedAt?.toEpochSeconds() ?: 0L, priority = PRIORITY_VISIBLE, - proFeatures = ProFeatures.NONE, + proFeatures = BitSet(), ) ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/MessageDetailActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/MessageDetailActivity.kt index 32ccaaa5bb..f49a5f79c6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/MessageDetailActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/MessageDetailActivity.kt @@ -66,6 +66,8 @@ import kotlinx.coroutines.launch import network.loki.messenger.R import network.loki.messenger.databinding.ViewVisibleMessageContentBinding import network.loki.messenger.libsession_util.protocol.ProFeature +import network.loki.messenger.libsession_util.protocol.ProMessageFeature +import network.loki.messenger.libsession_util.protocol.ProProfileFeature import org.session.libsession.database.StorageProtocol import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment import org.session.libsession.utilities.NonTranslatableStringConstants @@ -311,7 +313,7 @@ fun CellMetadata( verticalArrangement = Arrangement.spacedBy(LocalDimensions.current.smallSpacing) ) { // Message Pro features - if(proFeatures.isNotEmpty()) { + if (!proFeatures.isEmpty()) { MessageProFeatures( features = proFeatures, badgeClickable = proBadgeClickable, @@ -398,18 +400,18 @@ fun MessageProFeatures( style = LocalType.current.large ) - features.forEach { + features.forEach { feature -> ProCTAFeature( textStyle = LocalType.current.large, padding = PaddingValues(), data = CTAFeature.Icon( - text = when (it){ - ProFeature.PRO_BADGE -> Phrase.from(LocalContext.current, R.string.appProBadge) + text = when (feature){ + ProProfileFeature.PRO_BADGE -> Phrase.from(LocalContext.current, R.string.appProBadge) .put(APP_PRO_KEY, NonTranslatableStringConstants.APP_PRO) .format() .toString() - ProFeature.HIGHER_CHARACTER_LIMIT -> stringResource(id = R.string.proIncreasedMessageLengthFeature) - ProFeature.ANIMATED_AVATAR -> stringResource(id = R.string.proAnimatedDisplayPictureFeature) + ProMessageFeature.HIGHER_CHARACTER_LIMIT -> stringResource(id = R.string.proIncreasedMessageLengthFeature) + ProProfileFeature.ANIMATED_AVATAR -> stringResource(id = R.string.proAnimatedDisplayPictureFeature) } ) ) @@ -422,7 +424,7 @@ fun MessageProFeatures( fun PreviewMessageProFeatures(){ PreviewTheme { MessageProFeatures( - features = ProFeature.entries.toSet(), + features = (ProMessageFeature.entries + ProProfileFeature.entries).toSet(), badgeClickable = false, sendCommand = {} ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/MessageDetailsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/MessageDetailsViewModel.kt index 3327dc1415..5087e5b370 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/MessageDetailsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/MessageDetailsViewModel.kt @@ -26,6 +26,9 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import network.loki.messenger.R import network.loki.messenger.libsession_util.protocol.ProFeature +import network.loki.messenger.libsession_util.protocol.ProMessageFeature +import network.loki.messenger.libsession_util.protocol.ProProfileFeature +import network.loki.messenger.libsession_util.util.asSequence import org.session.libsession.messaging.groups.LegacyGroupDeprecationManager import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment import org.session.libsession.utilities.Address @@ -200,7 +203,7 @@ class MessageDetailsViewModel @AssistedInject constructor( senderIsBlinded = IdPrefix.fromValue(sender.address.toString())?.isBlinded() ?: false, thread = conversation, readOnly = isDeprecatedLegacyGroup, - proFeatures = proStatusManager.getMessageProFeatures(messageRecord).toSet(), + proFeatures = proStatusManager.getMessageProFeatures(messageRecord).asSequence().toSet(), proBadgeClickable = !recipientRepository.getSelf().isPro // no badge click if the current user is pro ) } @@ -284,8 +287,8 @@ class MessageDetailsViewModel @AssistedInject constructor( proBadgeCTA = when{ features.size > 1 -> ProBadgeCTA.Generic(proSubscription) // always show the generic cta when there are more than 1 feature - features.contains(ProFeature.HIGHER_CHARACTER_LIMIT) -> ProBadgeCTA.LongMessage(proSubscription) - features.contains(ProFeature.ANIMATED_AVATAR) -> ProBadgeCTA.AnimatedProfile(proSubscription) + features.contains(ProMessageFeature.HIGHER_CHARACTER_LIMIT) -> ProBadgeCTA.LongMessage(proSubscription) + features.contains(ProProfileFeature.ANIMATED_AVATAR) -> ProBadgeCTA.AnimatedProfile(proSubscription) else -> ProBadgeCTA.Generic(proSubscription) } ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientRepository.kt index 7baa3b5a15..b0846c1d69 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientRepository.kt @@ -28,7 +28,7 @@ import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.withContext import network.loki.messenger.libsession_util.PRIORITY_VISIBLE import network.loki.messenger.libsession_util.ReadableGroupInfoConfig -import network.loki.messenger.libsession_util.protocol.ProFeature +import network.loki.messenger.libsession_util.protocol.ProProfileFeature import network.loki.messenger.libsession_util.util.ExpiryMode import network.loki.messenger.libsession_util.util.GroupInfo import org.session.libsession.messaging.open_groups.GroupMemberRole @@ -587,7 +587,7 @@ class RecipientRepository @Inject constructor( proDataContext?.addProData( RecipientSettings.ProData( showProBadge = configs.userProfile.getProFeatures().contains( - ProFeature.PRO_BADGE + ProProfileFeature.PRO_BADGE ), expiry = Instant.ofEpochMilli(pro.proProof.expiryMs), genIndexHash = pro.proProof.genIndexHashHex, @@ -615,7 +615,7 @@ class RecipientRepository @Inject constructor( if (convo?.proProofInfo != null && proDataContext != null) { proDataContext.addProData( RecipientSettings.ProData( - showProBadge = contact.proFeatures.contains(ProFeature.PRO_BADGE), + showProBadge = contact.proFeatures.contains(ProProfileFeature.PRO_BADGE), expiry = convo.proProofInfo!!.expiry, genIndexHash = convo.proProofInfo!!.genIndexHash.data.toHexString(), ) @@ -682,7 +682,7 @@ class RecipientRepository @Inject constructor( if (convo?.proProofInfo != null && proDataContext != null) { proDataContext.addProData( RecipientSettings.ProData( - showProBadge = contact.proFeatures.contains(ProFeature.PRO_BADGE), + showProBadge = contact.proFeatures.contains(ProProfileFeature.PRO_BADGE), expiry = convo.proProofInfo!!.expiry, genIndexHash = convo.proProofInfo!!.genIndexHash.data.toHexString(), ) 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 c86eedc792..1d3ef8fa50 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -8,7 +8,7 @@ import network.loki.messenger.libsession_util.PRIORITY_PINNED import network.loki.messenger.libsession_util.PRIORITY_VISIBLE import network.loki.messenger.libsession_util.MutableConversationVolatileConfig import network.loki.messenger.libsession_util.ReadableUserGroupsConfig -import network.loki.messenger.libsession_util.protocol.ProFeatures +import network.loki.messenger.libsession_util.util.BitSet import network.loki.messenger.libsession_util.util.BlindKeyAPI import network.loki.messenger.libsession_util.util.Bytes import network.loki.messenger.libsession_util.util.Conversation @@ -834,7 +834,7 @@ open class Storage @Inject constructor( hasMention = false, isOpenGroupInvitation = false, isSecureMessage = false, - proFeatures = ProFeatures.NONE, + proFeatures = BitSet(), isGroupMessage = true, isGroupUpdateMessage = true, ) @@ -1051,7 +1051,7 @@ open class Storage @Inject constructor( null, null, emptyList(), - ProFeatures.NONE, + BitSet(), null, null, emptyList(), @@ -1077,7 +1077,7 @@ open class Storage @Inject constructor( body = null, group = null, attachments = emptyList(), - proFeatures = ProFeatures.NONE, + proFeatures = BitSet(), messageContent = null, quote = null, linkPreviews = emptyList(), diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecords.kt b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecords.kt index 9d899a7516..fc4a22eab2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecords.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecords.kt @@ -1,5 +1,6 @@ package org.thoughtcrime.securesms.database.model -import network.loki.messenger.libsession_util.protocol.ProFeatures +import network.loki.messenger.libsession_util.protocol.ProMessageFeature +import network.loki.messenger.libsession_util.util.BitSet -val MessageRecord.proFeatures: ProFeatures get() = ProFeatures(proFeaturesRawValue) \ No newline at end of file +val MessageRecord.proFeatures: BitSet get() = BitSet(proFeaturesRawValue) \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/ReactionRecord.kt b/app/src/main/java/org/thoughtcrime/securesms/database/model/ReactionRecord.kt index bbe7d854b6..18b02032ca 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/ReactionRecord.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/ReactionRecord.kt @@ -6,6 +6,12 @@ data class ReactionRecord( val author: String, val emoji: String, val serverId: String = "", + /** + * Count of this emoji per message. Note that this means that multiple rows with the same + * messageId and emoji will have the same count value (due to having different author). + * + * So you MUST NOT sum these counts across rows for one message. + */ val count: Long = 0, val sortId: Long = 0, val dateSent: Long = 0, diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/RecipientSettings.kt b/app/src/main/java/org/thoughtcrime/securesms/database/model/RecipientSettings.kt index 501b5c6229..a0780bd64f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/RecipientSettings.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/RecipientSettings.kt @@ -1,6 +1,10 @@ package org.thoughtcrime.securesms.database.model import kotlinx.serialization.Serializable +import network.loki.messenger.libsession_util.protocol.ProFeature +import network.loki.messenger.libsession_util.protocol.ProProfileFeature +import network.loki.messenger.libsession_util.util.BitSet +import network.loki.messenger.libsession_util.util.Conversation import network.loki.messenger.libsession_util.util.UserPic import org.session.libsession.utilities.serializable.InstantAsMillisSerializer import java.time.Instant @@ -25,6 +29,15 @@ data class RecipientSettings( val genIndexHash: String, val showProBadge: Boolean, ) { + + constructor( + info: Conversation.ProProofInfo, + features: BitSet, + ): this( + expiry = info.expiry, + genIndexHash = info.genIndexHash.data.toHexString(), + showProBadge = features.contains(ProProfileFeature.PRO_BADGE), + ) fun isExpired(now: Instant): Boolean { return expiry <= now } diff --git a/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenu.kt b/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenu.kt index a360be39dc..cbe98ada34 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenu.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenu.kt @@ -54,8 +54,8 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import network.loki.messenger.BuildConfig import network.loki.messenger.R -import network.loki.messenger.libsession_util.protocol.ProFeature -import network.loki.messenger.libsession_util.protocol.ProFeatures +import network.loki.messenger.libsession_util.protocol.ProMessageFeature +import network.loki.messenger.libsession_util.util.toBitSet import org.session.libsession.messaging.groups.LegacyGroupDeprecationManager import org.thoughtcrime.securesms.debugmenu.DebugMenuViewModel.Commands.* import org.thoughtcrime.securesms.debugmenu.DebugMenuViewModel.Companion.FALSE @@ -349,7 +349,7 @@ fun DebugMenu( AnimatedVisibility(uiState.forceIncomingMessagesAsPro) { Column { - for (feature in ProFeature.entries) { + for (feature in ProMessageFeature.entries) { DebugCheckboxRow( text = "Message Feature: ${feature.name}", minHeight = 30.dp, @@ -938,7 +938,7 @@ fun PreviewDebugMenu() { forceOtherUsersAsPro = false, forcePostPro = false, forceShortTTl = false, - messageProFeature = ProFeatures.from(listOf(ProFeature.ANIMATED_AVATAR)), + messageProFeature = listOf(ProMessageFeature.HIGHER_CHARACTER_LIMIT).toBitSet(), dbInspectorState = DebugMenuViewModel.DatabaseInspectorState.STARTED, debugSubscriptionStatuses = setOf(DebugMenuViewModel.DebugSubscriptionStatus.AUTO_GOOGLE), selectedDebugSubscriptionStatus = DebugMenuViewModel.DebugSubscriptionStatus.AUTO_GOOGLE, diff --git a/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuViewModel.kt index 9f2bf9091f..aa5a6eb653 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/debugmenu/DebugMenuViewModel.kt @@ -25,9 +25,11 @@ import kotlinx.coroutines.withContext import network.loki.messenger.libsession_util.PRIORITY_HIDDEN import network.loki.messenger.libsession_util.PRIORITY_VISIBLE import network.loki.messenger.libsession_util.ED25519 -import network.loki.messenger.libsession_util.protocol.ProFeature -import network.loki.messenger.libsession_util.protocol.ProFeatures +import network.loki.messenger.libsession_util.protocol.ProMessageFeature +import network.loki.messenger.libsession_util.util.BitSet import network.loki.messenger.libsession_util.util.BlindKeyAPI +import network.loki.messenger.libsession_util.util.asSequence +import network.loki.messenger.libsession_util.util.toBitSet import org.session.libsession.database.StorageProtocol import org.session.libsession.messaging.file_server.FileServer import org.session.libsession.messaging.file_server.FileServerApi @@ -356,9 +358,9 @@ class DebugMenuViewModel @AssistedInject constructor( } is Commands.SetMessageProFeature -> { - val features = _uiState.value.messageProFeature.toSet().toMutableSet() + val features = _uiState.value.messageProFeature.asSequence().toMutableSet() if(command.set) features.add(command.feature) else features.remove(command.feature) - val newFeatures = ProFeatures.from(features) + val newFeatures = features.toBitSet() textSecurePreferences.setDebugMessageFeatures(newFeatures) _uiState.update { it.copy(messageProFeature = newFeatures) @@ -617,7 +619,7 @@ class DebugMenuViewModel @AssistedInject constructor( val forceCurrentUserAsPro: Boolean, val forceOtherUsersAsPro: Boolean, val forceIncomingMessagesAsPro: Boolean, - val messageProFeature: ProFeatures, + val messageProFeature: BitSet, val forcePostPro: Boolean, val forceShortTTl: Boolean, val forceDeprecationState: LegacyGroupDeprecationManager.DeprecationState?, @@ -689,7 +691,7 @@ class DebugMenuViewModel @AssistedInject constructor( data class WithinQuickRefund(val set: Boolean) : Commands() data class ForcePostPro(val set: Boolean) : Commands() data class ForceShortTTl(val set: Boolean) : Commands() - data class SetMessageProFeature(val feature: ProFeature, val set: Boolean) : Commands() + data class SetMessageProFeature(val feature: ProMessageFeature, val set: Boolean) : Commands() data class ShowDeprecationChangeDialog(val state: LegacyGroupDeprecationManager.DeprecationState?) : Commands() object HideDeprecationChangeDialog : Commands() object OverrideDeprecationState : Commands() diff --git a/app/src/main/java/org/thoughtcrime/securesms/pro/ProStatusManager.kt b/app/src/main/java/org/thoughtcrime/securesms/pro/ProStatusManager.kt index a918c8b086..11436b1f49 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/pro/ProStatusManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/pro/ProStatusManager.kt @@ -31,7 +31,8 @@ import network.loki.messenger.libsession_util.pro.BackendRequests import network.loki.messenger.libsession_util.pro.BackendRequests.PAYMENT_PROVIDER_APP_STORE import network.loki.messenger.libsession_util.pro.BackendRequests.PAYMENT_PROVIDER_GOOGLE_PLAY import network.loki.messenger.libsession_util.pro.ProConfig -import network.loki.messenger.libsession_util.protocol.ProFeatures +import network.loki.messenger.libsession_util.protocol.ProMessageFeature +import network.loki.messenger.libsession_util.util.BitSet import network.loki.messenger.libsession_util.util.Conversation import org.session.libsession.messaging.messages.visible.VisibleMessage import org.session.libsession.snode.SnodeClock @@ -399,7 +400,7 @@ class ProStatusManager @Inject constructor( /** * This will get the list of Pro features from an incoming message */ - fun getMessageProFeatures(message: MessageRecord): ProFeatures { + fun getMessageProFeatures(message: MessageRecord): BitSet { // use debug values if any if(prefs.forceIncomingMessagesAsPro()){ return prefs.getDebugMessageFeatures() diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.kt b/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.kt index 98c9453f8a..1a05e6547f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/ExpiringMessageManager.kt @@ -8,7 +8,7 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.joinAll import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeoutOrNull -import network.loki.messenger.libsession_util.protocol.ProFeatures +import network.loki.messenger.libsession_util.util.BitSet import network.loki.messenger.libsession_util.util.ExpiryMode import org.session.libsession.messaging.messages.Message import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate @@ -100,7 +100,7 @@ class ExpiringMessageManager @Inject constructor( body = null, group = groupAddress, attachments = emptyList(), - proFeatures = ProFeatures.NONE, + proFeatures = BitSet(), messageContent = DisappearingMessageUpdate(message.expiryMode), quote = null, linkPreviews = emptyList(), From 055f6512ba6a44244a5f30eea444addeb0307c91 Mon Sep 17 00:00:00 2001 From: SessionHero01 <180888785+SessionHero01@users.noreply.github.com> Date: Thu, 27 Nov 2025 09:17:58 +1100 Subject: [PATCH 2/3] Fix remaining compile issues --- .../sending_receiving/MessageParser.kt | 60 +++++-------------- .../utilities/TextSecurePreferences.kt | 2 +- 2 files changed, 16 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/org/session/libsession/messaging/sending_receiving/MessageParser.kt b/app/src/main/java/org/session/libsession/messaging/sending_receiving/MessageParser.kt index c465f9059b..1b33d234f9 100644 --- a/app/src/main/java/org/session/libsession/messaging/sending_receiving/MessageParser.kt +++ b/app/src/main/java/org/session/libsession/messaging/sending_receiving/MessageParser.kt @@ -1,10 +1,11 @@ package org.session.libsession.messaging.sending_receiving -import network.loki.messenger.libsession_util.ED25519 +import dagger.Lazy import network.loki.messenger.libsession_util.SessionEncrypt -import network.loki.messenger.libsession_util.pro.ProProof import network.loki.messenger.libsession_util.protocol.DecodedEnvelope +import network.loki.messenger.libsession_util.protocol.DecodedPro import network.loki.messenger.libsession_util.protocol.SessionProtocol +import network.loki.messenger.libsession_util.util.BitSet import org.session.libsession.database.StorageProtocol import org.session.libsession.messaging.messages.Message import org.session.libsession.messaging.messages.control.CallMessage @@ -20,13 +21,14 @@ import org.session.libsession.messaging.open_groups.OpenGroupApi import org.session.libsession.snode.SnodeClock import org.session.libsession.utilities.Address import org.session.libsession.utilities.ConfigFactoryProtocol +import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.exceptions.NonRetryableException import org.session.libsignal.protos.SignalServiceProtos import org.session.libsignal.utilities.AccountId import org.session.libsignal.utilities.Base64 import org.session.libsignal.utilities.Hex import org.session.libsignal.utilities.IdPrefix -import java.time.Instant +import org.thoughtcrime.securesms.pro.ProStatusManager import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Singleton @@ -37,6 +39,7 @@ class MessageParser @Inject constructor( private val configFactory: ConfigFactoryProtocol, private val storage: StorageProtocol, private val snodeClock: SnodeClock, + private val prefs: TextSecurePreferences, ) { //TODO: Obtain proBackendKey from somewhere @@ -76,20 +79,10 @@ class MessageParser @Inject constructor( currentUserBlindedIDs: List, senderIdPrefix: IdPrefix ): Pair { - val proFeatures = if (decodedEnvelope.proProof?.status( - senderED25519PubKey = decodedEnvelope.senderEd25519PubKey.data, - signedMessage = null, - now = decodedEnvelope.timestamp) == ProProof.Status.Valid - ) { - decodedEnvelope.proFeatures - } else { - ProFeatures.NONE - } - return parseMessage( sender = AccountId(senderIdPrefix, decodedEnvelope.senderX25519PubKey.data), contentPlaintext = decodedEnvelope.contentPlainText.data, - proFeatures = proFeatures, + pro = decodedEnvelope.decodedPro, messageTimestampMs = decodedEnvelope.timestamp.toEpochMilli(), relaxSignatureCheck = relaxSignatureCheck, checkForBlockStatus = checkForBlockStatus, @@ -102,7 +95,7 @@ class MessageParser @Inject constructor( private fun parseMessage( sender: AccountId, contentPlaintext: ByteArray, - proFeatures: ProFeatures, + pro: DecodedPro?, messageTimestampMs: Long, relaxSignatureCheck: Boolean, checkForBlockStatus: Boolean, @@ -141,7 +134,11 @@ class MessageParser @Inject constructor( message.sentTimestamp = messageTimestampMs message.receivedTimestamp = snodeClock.currentTimeMills() message.isSenderSelf = isSenderSelf - (message as? VisibleMessage)?.proFeatures = proFeatures + + // Only process pro features post pro launch + if (prefs.forcePostPro()) { + (message as? VisibleMessage)?.proFeatures = pro?.proMessageFeatures ?: BitSet() + } // Validate var isValid = message.isValid() @@ -243,19 +240,9 @@ class MessageParser @Inject constructor( val sender = AccountId(msg.sessionId) - val proFeatures = if (decoded.proProof?.status( - senderED25519PubKey = sender.pubKeyBytes, - signedMessage = null, - now = Instant.ofEpochMilli((msg.posted * 1000.0).toLong())) == ProProof.Status.Valid - ) { - decoded.proFeatures - } else { - ProFeatures.NONE - } - return parseMessage( contentPlaintext = decoded.contentPlainText.data, - proFeatures = proFeatures, + pro = decoded.decodedPro, relaxSignatureCheck = true, checkForBlockStatus = false, isForGroup = false, @@ -290,27 +277,10 @@ class MessageParser @Inject constructor( ) val sender = Address.Standard(AccountId(senderId)) - val messageSent = Instant.ofEpochMilli((msg.postedAt * 1000.0).toLong()) - - val proProof = decoded.proProof - val proFeatures = if (proProof != null) { - val hasValidProof = ED25519.ed25519PubKeysFromCurve25519(sender.accountId.pubKeyBytes) - .any { senderEd25519PubKey -> - proProof.status(senderEd25519PubKey, now = messageSent) == ProProof.Status.Valid - } - - if (hasValidProof) { - decoded.proFeatures - } else { - ProFeatures.NONE - } - } else { - ProFeatures.NONE - } return parseMessage( contentPlaintext = decoded.contentPlainText.data, - proFeatures = proFeatures, + pro = decoded.decodedPro, relaxSignatureCheck = true, checkForBlockStatus = false, isForGroup = false, diff --git a/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt b/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt index 7670926387..e0d3d85c22 100644 --- a/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt +++ b/app/src/main/java/org/session/libsession/utilities/TextSecurePreferences.kt @@ -1557,7 +1557,7 @@ class AppTextSecurePreferences @Inject constructor( } override fun forcePostPro(): Boolean { - return getBooleanPreference(SET_FORCE_POST_PRO, false) + return postProLaunchState.value } override fun setForcePostPro(postPro: Boolean) { From 3020a22635c6c94e3171f765929c395de97c7d2c Mon Sep 17 00:00:00 2001 From: SessionHero01 <180888785+SessionHero01@users.noreply.github.com> Date: Thu, 27 Nov 2025 09:41:22 +1100 Subject: [PATCH 3/3] Update libsession-util version --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3f484b9ce9..6f4947f369 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -29,7 +29,7 @@ kotlinVersion = "2.2.21" kryoVersion = "5.6.2" kspVersion = "2.3.3" legacySupportV13Version = "1.0.0" -libsessionUtilAndroidVersion = "1.0.9-5-g10064f8" +libsessionUtilAndroidVersion = "1.0.9-7-gf2e3e9b" media3ExoplayerVersion = "1.8.0" mockitoCoreVersion = "5.20.0" navVersion = "2.9.5"