diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java index 2498e90b69..cca0f162c6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java @@ -62,13 +62,14 @@ public class RecipientDatabase extends Database { private static final String UNIDENTIFIED_ACCESS_MODE = "unidentified_access_mode"; private static final String FORCE_SMS_SELECTION = "force_sms_selection"; private static final String NOTIFY_TYPE = "notify_type"; // all, mentions only, none + private static final String WRAPPER_HASH = "wrapper_hash"; private static final String[] RECIPIENT_PROJECTION = new String[] { BLOCK, APPROVED, APPROVED_ME, NOTIFICATION, CALL_RINGTONE, VIBRATE, CALL_VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, EXPIRE_MESSAGES, REGISTERED, PROFILE_KEY, SYSTEM_DISPLAY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_CONTACT_URI, SIGNAL_PROFILE_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, NOTIFICATION_CHANNEL, UNIDENTIFIED_ACCESS_MODE, - FORCE_SMS_SELECTION, NOTIFY_TYPE, + FORCE_SMS_SELECTION, NOTIFY_TYPE, WRAPPER_HASH }; static final List TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION) @@ -136,6 +137,11 @@ public class RecipientDatabase extends Database { "OR "+ADDRESS+" IN (SELECT "+GroupDatabase.TABLE_NAME+"."+GroupDatabase.ADMINS+" FROM "+GroupDatabase.TABLE_NAME+")))"; } + public static String getAddWrapperHash() { + return "ALTER TABLE "+TABLE_NAME+" "+ + "ADD COLUMN "+WRAPPER_HASH+" TEXT DEFAULT NULL;"; + } + public static final int NOTIFY_TYPE_ALL = 0; public static final int NOTIFY_TYPE_MENTIONS = 1; public static final int NOTIFY_TYPE_NONE = 2; @@ -190,6 +196,7 @@ public class RecipientDatabase extends Database { String notificationChannel = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION_CHANNEL)); int unidentifiedAccessMode = cursor.getInt(cursor.getColumnIndexOrThrow(UNIDENTIFIED_ACCESS_MODE)); boolean forceSmsSelection = cursor.getInt(cursor.getColumnIndexOrThrow(FORCE_SMS_SELECTION)) == 1; + String wrapperHash = cursor.getString(cursor.getColumnIndexOrThrow(WRAPPER_HASH)); MaterialColor color; byte[] profileKey = null; @@ -221,7 +228,7 @@ public class RecipientDatabase extends Database { systemPhoneLabel, systemContactUri, signalProfileName, signalProfileAvatar, profileSharing, notificationChannel, Recipient.UnidentifiedAccessMode.fromMode(unidentifiedAccessMode), - forceSmsSelection)); + forceSmsSelection, wrapperHash)); } public void setColor(@NonNull Recipient recipient, @NonNull MaterialColor color) { @@ -258,6 +265,14 @@ public class RecipientDatabase extends Database { return false; } + public void setRecipientHash(@NonNull Recipient recipient, String recipientHash) { + ContentValues values = new ContentValues(); + values.put(WRAPPER_HASH, recipientHash); + updateOrInsert(recipient.getAddress(), values); + recipient.resolve().setWrapperHash(recipientHash); + notifyRecipientListeners(); + } + public void setApproved(@NonNull Recipient recipient, boolean approved) { ContentValues values = new ContentValues(); values.put(APPROVED, approved ? 1 : 0); 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 5989cfcc6b..5867afc108 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -218,6 +218,7 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co override fun markConversationAsRead(threadId: Long, lastSeenTime: Long) { val threadDb = DatabaseComponent.get(context).threadDatabase() getRecipientForThread(threadId)?.let { recipient -> + val currentLastRead = threadDb.getLastSeenAndHasSent(threadId).first() // don't set the last read in the volatile if we didn't set it in the DB if (!threadDb.markAllAsRead(threadId, recipient.isGroupRecipient, lastSeenTime)) return @@ -246,6 +247,10 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co else -> throw NullPointerException("Weren't expecting to have a convo with address ${recipient.address.serialize()}") } convo.lastRead = lastSeenTime + if (convo.unread) { + convo.unread = lastSeenTime <= currentLastRead + notifyConversationListListeners() + } config.set(convo) ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context) } @@ -1088,8 +1093,11 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co override fun setContact(contact: Contact) { DatabaseComponent.get(context).sessionContactDatabase().setContact(contact) - if (!getRecipientApproved(Address.fromSerialized(contact.sessionID))) return - SSKEnvironment.shared.profileManager.contactUpdatedInternal(contact) + val address = fromSerialized(contact.sessionID) + if (!getRecipientApproved(address)) return + val recipientHash = SSKEnvironment.shared.profileManager.contactUpdatedInternal(contact) + val recipient = Recipient.from(context, address, false) + setRecipientHash(recipient, recipientHash) } override fun getRecipientForThread(threadId: Long): Recipient? { @@ -1140,6 +1148,7 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co setPinned(conversationThreadId, contact.priority == PRIORITY_PINNED) } } + setRecipientHash(recipient, contact.hashCode().toString()) } } @@ -1184,6 +1193,11 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co } } + override fun setRecipientHash(recipient: Recipient, recipientHash: String?) { + val recipientDb = DatabaseComponent.get(context).recipientDatabase() + recipientDb.setRecipientHash(recipient, recipientHash) + } + override fun getLastUpdated(threadID: Long): Long { val threadDB = DatabaseComponent.get(context).threadDatabase() return threadDB.getLastUpdated(threadID) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index bea4323250..5a59b1642f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -89,9 +89,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { private static final int lokiV39 = 60; private static final int lokiV40 = 61; private static final int lokiV41 = 62; + private static final int lokiV42 = 63; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes - private static final int DATABASE_VERSION = lokiV41; + private static final int DATABASE_VERSION = lokiV42; private static final int MIN_DATABASE_VERSION = lokiV7; private static final String CIPHER3_DATABASE_NAME = "signal.db"; public static final String DATABASE_NAME = "signal_v4.db"; @@ -359,7 +360,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { executeStatements(db, ReactionDatabase.CREATE_INDEXS); executeStatements(db, ReactionDatabase.CREATE_REACTION_TRIGGERS); -// db.execSQL(ThreadDatabase.getCreateLastSeenTrigger()); + db.execSQL(RecipientDatabase.getAddWrapperHash()); } @Override @@ -600,6 +601,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { TextSecurePreferences.setForceNewConfig(context); } + if (oldVersion < lokiV42) { + db.execSQL(RecipientDatabase.getAddWrapperHash()); + } + db.setTransactionSuccessful(); } finally { db.endTransaction(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/ConversationOptionsBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/home/ConversationOptionsBottomSheet.kt index fc85f544fe..cb00a99508 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/ConversationOptionsBottomSheet.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/ConversationOptionsBottomSheet.kt @@ -7,10 +7,15 @@ import android.view.View import android.view.ViewGroup import androidx.core.view.isVisible import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import dagger.hilt.android.AndroidEntryPoint import network.loki.messenger.databinding.FragmentConversationBottomSheetBinding import org.thoughtcrime.securesms.database.model.ThreadRecord +import org.thoughtcrime.securesms.dependencies.ConfigFactory import org.thoughtcrime.securesms.util.UiModeUtilities +import org.thoughtcrime.securesms.util.getConversationUnread +import javax.inject.Inject +@AndroidEntryPoint class ConversationOptionsBottomSheet(private val parentContext: Context) : BottomSheetDialogFragment(), View.OnClickListener { private lateinit var binding: FragmentConversationBottomSheetBinding //FIXME AC: Supplying a threadRecord directly into the field from an activity @@ -19,6 +24,8 @@ class ConversationOptionsBottomSheet(private val parentContext: Context) : Botto // if we want to use dialog fragments properly. lateinit var thread: ThreadRecord + @Inject lateinit var configFactory: ConfigFactory + var onViewDetailsTapped: (() -> Unit?)? = null var onCopyConversationId: (() -> Unit?)? = null var onPinTapped: (() -> Unit)? = null @@ -77,7 +84,7 @@ class ConversationOptionsBottomSheet(private val parentContext: Context) : Botto binding.notificationsTextView.isVisible = recipient.isGroupRecipient && !recipient.isMuted binding.notificationsTextView.setOnClickListener(this) binding.deleteTextView.setOnClickListener(this) - binding.markAllAsReadTextView.isVisible = thread.unreadCount > 0 + binding.markAllAsReadTextView.isVisible = thread.unreadCount > 0 || configFactory.convoVolatile?.getConversationUnread(thread) == true binding.markAllAsReadTextView.setOnClickListener(this) binding.pinTextView.isVisible = !thread.isPinned binding.unpinTextView.isVisible = thread.isPinned diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/ConversationView.kt b/app/src/main/java/org/thoughtcrime/securesms/home/ConversationView.kt index 4b087fe283..8d5acb244d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/ConversationView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/ConversationView.kt @@ -11,6 +11,7 @@ import android.widget.LinearLayout import androidx.core.content.ContextCompat import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView +import dagger.hilt.android.AndroidEntryPoint import network.loki.messenger.R import network.loki.messenger.databinding.ViewConversationBinding import org.session.libsession.utilities.recipients.Recipient @@ -18,12 +19,19 @@ import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities.hig import org.thoughtcrime.securesms.database.RecipientDatabase.NOTIFY_TYPE_ALL import org.thoughtcrime.securesms.database.RecipientDatabase.NOTIFY_TYPE_NONE import org.thoughtcrime.securesms.database.model.ThreadRecord +import org.thoughtcrime.securesms.dependencies.ConfigFactory import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.util.DateUtils import org.thoughtcrime.securesms.util.getAccentColor +import org.thoughtcrime.securesms.util.getConversationUnread import java.util.Locale +import javax.inject.Inject +@AndroidEntryPoint class ConversationView : LinearLayout { + + @Inject lateinit var configFactory: ConfigFactory + private val binding: ViewConversationBinding by lazy { ViewConversationBinding.bind(this) } private val screenWidth = Resources.getSystem().displayMetrics.widthPixels var thread: ThreadRecord? = null @@ -70,7 +78,7 @@ class ConversationView : LinearLayout { // This would also not trigger the disappearing message timer which may or may not be desirable binding.accentView.visibility = if (unreadCount > 0 && !thread.isRead) View.VISIBLE else View.INVISIBLE } - val formattedUnreadCount = if (thread.isRead) { + val formattedUnreadCount = if (unreadCount == 0) { null } else { if (unreadCount < 10000) unreadCount.toString() else "9999+" @@ -79,6 +87,7 @@ class ConversationView : LinearLayout { val textSize = if (unreadCount < 1000) 12.0f else 10.0f binding.unreadCountTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, textSize) binding.unreadCountIndicator.isVisible = (unreadCount != 0 && !thread.isRead) + || (configFactory.convoVolatile?.getConversationUnread(thread) == true) binding.unreadMentionTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, textSize) binding.unreadMentionIndicator.isVisible = (thread.unreadMentionCount != 0 && thread.recipient.address.isGroup) val senderDisplayName = getUserDisplayName(thread.recipient) diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt index 1d20524290..9a3f92f038 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt @@ -106,7 +106,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), get() = textSecurePreferences.getLocalNumber()!! private val homeAdapter: HomeAdapter by lazy { - HomeAdapter(context = this, listener = this) + HomeAdapter(context = this, configFactory = configFactory, listener = this) } private val globalSearchAdapter = GlobalSearchAdapter { model -> diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/HomeAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomeAdapter.kt index 4273794f5e..eaf242aae3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/HomeAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeAdapter.kt @@ -10,10 +10,12 @@ import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.NO_ID import network.loki.messenger.R import org.thoughtcrime.securesms.database.model.ThreadRecord +import org.thoughtcrime.securesms.dependencies.ConfigFactory import org.thoughtcrime.securesms.mms.GlideRequests class HomeAdapter( private val context: Context, + private val configFactory: ConfigFactory, private val listener: ConversationClickListener ) : RecyclerView.Adapter(), ListUpdateCallback { @@ -29,7 +31,7 @@ class HomeAdapter( get() = _data.toList() set(newData) { val previousData = _data.toList() - val diff = HomeDiffUtil(previousData, newData, context) + val diff = HomeDiffUtil(previousData, newData, context, configFactory) val diffResult = DiffUtil.calculateDiff(diff) _data = newData diffResult.dispatchUpdatesTo(this as ListUpdateCallback) diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/HomeDiffUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomeDiffUtil.kt index b883709c0a..0fe93d41de 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/HomeDiffUtil.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeDiffUtil.kt @@ -3,11 +3,14 @@ package org.thoughtcrime.securesms.home import android.content.Context import androidx.recyclerview.widget.DiffUtil import org.thoughtcrime.securesms.database.model.ThreadRecord +import org.thoughtcrime.securesms.dependencies.ConfigFactory +import org.thoughtcrime.securesms.util.getConversationUnread class HomeDiffUtil( private val old: List, private val new: List, - private val context: Context + private val context: Context, + private val configFactory: ConfigFactory ): DiffUtil.Callback() { override fun getOldListSize(): Int = old.size @@ -42,7 +45,9 @@ class HomeDiffUtil( oldItem.isFailed == newItem.isFailed && oldItem.isDelivered == newItem.isDelivered && oldItem.isSent == newItem.isSent && - oldItem.isPending == newItem.isPending + oldItem.isPending == newItem.isPending && + oldItem.lastSeen == newItem.lastSeen && + configFactory.convoVolatile?.getConversationUnread(newItem) != true ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ProfileManager.kt b/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ProfileManager.kt index fbbdebf6c0..d6e3761d7f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ProfileManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/sskenvironment/ProfileManager.kt @@ -83,12 +83,12 @@ class ProfileManager(private val context: Context, private val configFactory: Co database.setUnidentifiedAccessMode(recipient, unidentifiedAccessMode) } - override fun contactUpdatedInternal(contact: Contact) { - val contactConfig = configFactory.contacts ?: return - if (contact.sessionID == TextSecurePreferences.getLocalNumber(context)) return + override fun contactUpdatedInternal(contact: Contact): String? { + val contactConfig = configFactory.contacts ?: return null + if (contact.sessionID == TextSecurePreferences.getLocalNumber(context)) return null val sessionId = SessionId(contact.sessionID) - if (sessionId.prefix != IdPrefix.STANDARD) return // only internally store standard session IDs - if (contactConfig.get(contact.sessionID) == null) return // don't insert, only update + if (sessionId.prefix != IdPrefix.STANDARD) return null // only internally store standard session IDs + if (contactConfig.get(contact.sessionID) == null) return null // don't insert, only update contactConfig.upsertContact(contact.sessionID) { this.name = contact.name.orEmpty() this.nickname = contact.nickname.orEmpty() @@ -103,6 +103,7 @@ class ProfileManager(private val context: Context, private val configFactory: Co if (contactConfig.needsPush()) { ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context) } + return contactConfig.get(contact.sessionID)?.hashCode()?.toString() } } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/SharedConfigUtils.kt b/app/src/main/java/org/thoughtcrime/securesms/util/SharedConfigUtils.kt new file mode 100644 index 0000000000..699b250e0d --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/util/SharedConfigUtils.kt @@ -0,0 +1,19 @@ +package org.thoughtcrime.securesms.util + +import network.loki.messenger.libsession_util.ConversationVolatileConfig +import org.session.libsession.messaging.MessagingModuleConfiguration +import org.session.libsession.utilities.GroupUtil +import org.thoughtcrime.securesms.database.model.ThreadRecord + +fun ConversationVolatileConfig.getConversationUnread(thread: ThreadRecord): Boolean { + val recipient = thread.recipient + if (recipient.isContactRecipient) { + return getOneToOne(recipient.address.serialize())?.unread == true + } else if (recipient.isClosedGroupRecipient) { + return getLegacyClosedGroup(GroupUtil.doubleDecodeGroupId(recipient.address.toGroupString()))?.unread == true + } else if (recipient.isOpenGroupRecipient) { + val openGroup = MessagingModuleConfiguration.shared.storage.getOpenGroup(thread.threadId) ?: return false + return getCommunity(openGroup.server, openGroup.room)?.unread == true + } + return false +} \ No newline at end of file diff --git a/libsession-util/src/androidTest/java/network/loki/messenger/libsession_util/InstrumentedTests.kt b/libsession-util/src/androidTest/java/network/loki/messenger/libsession_util/InstrumentedTests.kt index 8b0777a6a4..dcb64bafae 100644 --- a/libsession-util/src/androidTest/java/network/loki/messenger/libsession_util/InstrumentedTests.kt +++ b/libsession-util/src/androidTest/java/network/loki/messenger/libsession_util/InstrumentedTests.kt @@ -47,6 +47,22 @@ class InstrumentedTests { assertArrayEquals(kp.secretKey.take(32).toByteArray(), seed) } + @Test + private fun testDirtyEmptyString() { + val contacts = Contacts.newInstance(keyPair.secretKey) + val definitelyRealId = "050000000000000000000000000000000000000000000000000000000000000000" + val contact = contacts.getOrConstruct(definitelyRealId) + contacts.set(contact) + assertTrue(contacts.dirty()) + contacts.set(contact.copy(name = "test")) + assertTrue(contacts.dirty()) + val push = contacts.push() + contacts.confirmPushed(push.seqNo, "abc123") + contacts.set(contact.copy(name = "test2")) + contacts.set(contact.copy(name = "test")) + assertFalse(contacts.dirty()) + } + @Test fun jni_contacts() { val contacts = Contacts.newInstance(keyPair.secretKey) diff --git a/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt b/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt index d180114e34..e457680dbd 100644 --- a/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt +++ b/libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt @@ -156,6 +156,7 @@ interface StorageProtocol { // Settings fun setProfileSharing(address: Address, value: Boolean) + // Thread fun getOrCreateThreadIdFor(address: Address): Long fun getThreadIdFor(publicKey: String, groupPublicKey: String?, openGroupID: String?, createThread: Boolean): Long? @@ -217,6 +218,7 @@ interface StorageProtocol { fun updateReactionIfNeeded(message: Message, sender: String, openGroupSentTimestamp: Long) fun deleteReactions(messageId: Long, mms: Boolean) fun setBlocked(recipients: List, isBlocked: Boolean, fromConfigUpdate: Boolean = false) + fun setRecipientHash(recipient: Recipient, recipientHash: String?) fun blockedContacts(): List // Shared configs diff --git a/libsession/src/main/java/org/session/libsession/utilities/SSKEnvironment.kt b/libsession/src/main/java/org/session/libsession/utilities/SSKEnvironment.kt index 1fe8e5a016..f647cc0f48 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/SSKEnvironment.kt +++ b/libsession/src/main/java/org/session/libsession/utilities/SSKEnvironment.kt @@ -33,7 +33,7 @@ class SSKEnvironment( fun setName(context: Context, recipient: Recipient, name: String?) fun setProfilePicture(context: Context, recipient: Recipient, profilePictureURL: String?, profileKey: ByteArray?) fun setUnidentifiedAccessMode(context: Context, recipient: Recipient, unidentifiedAccessMode: Recipient.UnidentifiedAccessMode) - fun contactUpdatedInternal(contact: Contact) + fun contactUpdatedInternal(contact: Contact): String? } interface MessageExpirationManagerProtocol { diff --git a/libsession/src/main/java/org/session/libsession/utilities/recipients/Recipient.java b/libsession/src/main/java/org/session/libsession/utilities/recipients/Recipient.java index 1e875fd2a1..f24840e7cc 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/recipients/Recipient.java +++ b/libsession/src/main/java/org/session/libsession/utilities/recipients/Recipient.java @@ -99,6 +99,7 @@ public class Recipient implements RecipientModifiedListener { private boolean profileSharing; private String notificationChannel; private boolean forceSmsSelection; + private String wrapperHash; private @NonNull UnidentifiedAccessMode unidentifiedAccessMode = UnidentifiedAccessMode.ENABLED; @@ -279,6 +280,7 @@ public class Recipient implements RecipientModifiedListener { this.profileSharing = details.profileSharing; this.unidentifiedAccessMode = details.unidentifiedAccessMode; this.forceSmsSelection = details.forceSmsSelection; + this.wrapperHash = details.wrapperHash; this.participants.addAll(details.participants); this.resolving = false; @@ -723,6 +725,14 @@ public class Recipient implements RecipientModifiedListener { return unidentifiedAccessMode; } + public String getWrapperHash() { + return wrapperHash; + } + + public void setWrapperHash(String wrapperHash) { + this.wrapperHash = wrapperHash; + } + public void setUnidentifiedAccessMode(@NonNull UnidentifiedAccessMode unidentifiedAccessMode) { synchronized (this) { this.unidentifiedAccessMode = unidentifiedAccessMode; @@ -745,12 +755,12 @@ public class Recipient implements RecipientModifiedListener { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Recipient recipient = (Recipient) o; - return resolving == recipient.resolving && mutedUntil == recipient.mutedUntil && notifyType == recipient.notifyType && blocked == recipient.blocked && approved == recipient.approved && approvedMe == recipient.approvedMe && expireMessages == recipient.expireMessages && address.equals(recipient.address) && Objects.equals(name, recipient.name) && Objects.equals(customLabel, recipient.customLabel) && Objects.equals(groupAvatarId, recipient.groupAvatarId) && Arrays.equals(profileKey, recipient.profileKey) && Objects.equals(profileName, recipient.profileName) && Objects.equals(profileAvatar, recipient.profileAvatar); + return resolving == recipient.resolving && mutedUntil == recipient.mutedUntil && notifyType == recipient.notifyType && blocked == recipient.blocked && approved == recipient.approved && approvedMe == recipient.approvedMe && expireMessages == recipient.expireMessages && address.equals(recipient.address) && Objects.equals(name, recipient.name) && Objects.equals(customLabel, recipient.customLabel) && Objects.equals(groupAvatarId, recipient.groupAvatarId) && Arrays.equals(profileKey, recipient.profileKey) && Objects.equals(profileName, recipient.profileName) && Objects.equals(profileAvatar, recipient.profileAvatar) && Objects.equals(wrapperHash, recipient.wrapperHash); } @Override public int hashCode() { - int result = Objects.hash(address, name, customLabel, resolving, groupAvatarId, mutedUntil, notifyType, blocked, approved, approvedMe, expireMessages, profileName, profileAvatar); + int result = Objects.hash(address, name, customLabel, resolving, groupAvatarId, mutedUntil, notifyType, blocked, approved, approvedMe, expireMessages, profileName, profileAvatar, wrapperHash); result = 31 * result + Arrays.hashCode(profileKey); return result; } @@ -854,6 +864,7 @@ public class Recipient implements RecipientModifiedListener { private final String notificationChannel; private final UnidentifiedAccessMode unidentifiedAccessMode; private final boolean forceSmsSelection; + private final String wrapperHash; public RecipientSettings(boolean blocked, boolean approved, boolean approvedMe, long muteUntil, int notifyType, @@ -875,7 +886,8 @@ public class Recipient implements RecipientModifiedListener { boolean profileSharing, @Nullable String notificationChannel, @NonNull UnidentifiedAccessMode unidentifiedAccessMode, - boolean forceSmsSelection) + boolean forceSmsSelection, + String wrapperHash) { this.blocked = blocked; this.approved = approved; @@ -901,6 +913,7 @@ public class Recipient implements RecipientModifiedListener { this.notificationChannel = notificationChannel; this.unidentifiedAccessMode = unidentifiedAccessMode; this.forceSmsSelection = forceSmsSelection; + this.wrapperHash = wrapperHash; } public @Nullable MaterialColor getColor() { @@ -998,6 +1011,11 @@ public class Recipient implements RecipientModifiedListener { public boolean isForceSmsSelection() { return forceSmsSelection; } + + public String getWrapperHash() { + return wrapperHash; + } + } diff --git a/libsession/src/main/java/org/session/libsession/utilities/recipients/RecipientProvider.java b/libsession/src/main/java/org/session/libsession/utilities/recipients/RecipientProvider.java index 03c225e207..75ebd837b6 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/recipients/RecipientProvider.java +++ b/libsession/src/main/java/org/session/libsession/utilities/recipients/RecipientProvider.java @@ -177,6 +177,7 @@ class RecipientProvider { @Nullable final String notificationChannel; @NonNull final UnidentifiedAccessMode unidentifiedAccessMode; final boolean forceSmsSelection; + final String wrapperHash; RecipientDetails(@Nullable String name, @Nullable Long groupAvatarId, boolean systemContact, boolean isLocalNumber, @Nullable RecipientSettings settings, @@ -209,6 +210,7 @@ class RecipientProvider { this.notificationChannel = settings != null ? settings.getNotificationChannel() : null; this.unidentifiedAccessMode = settings != null ? settings.getUnidentifiedAccessMode() : UnidentifiedAccessMode.DISABLED; this.forceSmsSelection = settings != null && settings.isForceSmsSelection(); + this.wrapperHash = settings != null ? settings.getWrapperHash() : null; if (name == null && settings != null) this.name = settings.getSystemDisplayName(); else this.name = name;