feat: adding support for new closed groups, moving closed groups to be legacy throughout app

This commit is contained in:
0x330a 2023-08-24 17:38:14 +10:00
parent 4e0d043a8c
commit eaddc44de1
No known key found for this signature in database
GPG Key ID: 267811D6E6A2698C
26 changed files with 150 additions and 112 deletions

View File

@ -20,7 +20,6 @@ import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor; import android.database.Cursor;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@ -34,9 +33,7 @@ import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ActionMode; import androidx.appcompat.view.ActionMode;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
@ -53,7 +50,6 @@ import androidx.viewpager.widget.ViewPager;
import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayout;
import org.session.libsession.database.StorageProtocol;
import org.session.libsession.messaging.MessagingModuleConfiguration; import org.session.libsession.messaging.MessagingModuleConfiguration;
import org.session.libsession.messaging.messages.control.DataExtractionNotification; import org.session.libsession.messaging.messages.control.DataExtractionNotification;
import org.session.libsession.messaging.sending_receiving.MessageSender; import org.session.libsession.messaging.sending_receiving.MessageSender;
@ -65,7 +61,6 @@ import org.session.libsession.utilities.Util;
import org.session.libsession.utilities.ViewUtil; import org.session.libsession.utilities.ViewUtil;
import org.session.libsession.utilities.recipients.Recipient; import org.session.libsession.utilities.recipients.Recipient;
import org.session.libsession.utilities.task.ProgressDialogAsyncTask; import org.session.libsession.utilities.task.ProgressDialogAsyncTask;
import org.thoughtcrime.securesms.conversation.settings.ClearAllMediaDialog;
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter; import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
import org.thoughtcrime.securesms.database.MediaDatabase; import org.thoughtcrime.securesms.database.MediaDatabase;
import org.thoughtcrime.securesms.database.loaders.ThreadMediaLoader; import org.thoughtcrime.securesms.database.loaders.ThreadMediaLoader;

View File

@ -54,7 +54,7 @@ class ProfilePictureView @JvmOverloads constructor(
return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey
} }
if (recipient.isClosedGroupRecipient) { if (recipient.isLegacyClosedGroupRecipient) {
val members = DatabaseComponent.get(context).groupDatabase() val members = DatabaseComponent.get(context).groupDatabase()
.getGroupMemberAddresses(recipient.address.toGroupString(), true) .getGroupMemberAddresses(recipient.address.toGroupString(), true)
.sorted() .sorted()

View File

@ -52,7 +52,7 @@ class ContactSelectionListLoader(context: Context, val mode: Int, val filter: St
private fun getClosedGroups(contacts: List<Recipient>): List<ContactSelectionListItem> { private fun getClosedGroups(contacts: List<Recipient>): List<ContactSelectionListItem> {
return getItems(contacts, context.getString(R.string.fragment_contact_selection_closed_groups_title)) { return getItems(contacts, context.getString(R.string.fragment_contact_selection_closed_groups_title)) {
it.address.isClosedGroup it.address.isLegacyClosedGroup || it.address.isClosedGroup
} }
} }

View File

@ -60,7 +60,6 @@ class ConversationSettingsActivity: PassphraseRequiredActionBarActivity(), View.
super.onCreate(savedInstanceState, ready) super.onCreate(savedInstanceState, ready)
binding = ActivityConversationSettingsBinding.inflate(layoutInflater) binding = ActivityConversationSettingsBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
binding.profilePictureView.root.glide = GlideApp.with(this)
updateRecipientDisplay() updateRecipientDisplay()
binding.searchConversation.setOnClickListener(this) binding.searchConversation.setOnClickListener(this)
binding.clearMessages.setOnClickListener(this) binding.clearMessages.setOnClickListener(this)
@ -91,7 +90,7 @@ class ConversationSettingsActivity: PassphraseRequiredActionBarActivity(), View.
} }
// Toggle group-specific settings // Toggle group-specific settings
val areGroupOptionsVisible = recipient.isClosedGroupRecipient val areGroupOptionsVisible = recipient.isClosedGroupRecipient || recipient.isLegacyClosedGroupRecipient
groupOptions.forEach { v -> groupOptions.forEach { v ->
v.isVisible = areGroupOptionsVisible v.isVisible = areGroupOptionsVisible
} }
@ -164,7 +163,7 @@ class ConversationSettingsActivity: PassphraseRequiredActionBarActivity(), View.
} }
v === binding.editGroup -> { v === binding.editGroup -> {
val recipient = viewModel.recipient ?: return val recipient = viewModel.recipient ?: return
if (!recipient.isClosedGroupRecipient) return if (!recipient.isClosedGroupRecipient || !recipient.isLegacyClosedGroupRecipient) return
val intent = Intent(this, EditClosedGroupActivity::class.java) val intent = Intent(this, EditClosedGroupActivity::class.java)
val groupID: String = recipient.address.toGroupString() val groupID: String = recipient.address.toGroupString()
intent.putExtra(EditClosedGroupActivity.groupIDKey, groupID) intent.putExtra(EditClosedGroupActivity.groupIDKey, groupID)

View File

@ -18,11 +18,11 @@ class ConversationSettingsViewModel(
val recipient get() = storage.getRecipientForThread(threadId) val recipient get() = storage.getRecipientForThread(threadId)
fun isPinned() = storage.isThreadPinned(threadId) fun isPinned() = storage.isPinned(threadId)
fun togglePin() = viewModelScope.launch { fun togglePin() = viewModelScope.launch {
val isPinned = storage.isThreadPinned(threadId) val isPinned = storage.isPinned(threadId)
storage.setThreadPinned(threadId, !isPinned) storage.setPinned(threadId, !isPinned)
} }
fun autoDownloadAttachments() = recipient?.let { recipient -> storage.shouldAutoDownloadAttachments(recipient) } ?: false fun autoDownloadAttachments() = recipient?.let { recipient -> storage.shouldAutoDownloadAttachments(recipient) } ?: false
@ -32,7 +32,7 @@ class ConversationSettingsViewModel(
} }
fun isUserGroupAdmin(): Boolean = recipient?.let { recipient -> fun isUserGroupAdmin(): Boolean = recipient?.let { recipient ->
if (!recipient.isClosedGroupRecipient) return@let false if (!recipient.isLegacyClosedGroupRecipient || !recipient.isClosedGroupRecipient) return@let false
val localUserAddress = prefs.getLocalNumber() ?: return@let false val localUserAddress = prefs.getLocalNumber() ?: return@let false
val group = storage.getGroup(recipient.address.toGroupString()) val group = storage.getGroup(recipient.address.toGroupString())
group?.admins?.contains(Address.fromSerialized(localUserAddress)) ?: false // this will have to be replaced for new closed groups group?.admins?.contains(Address.fromSerialized(localUserAddress)) ?: false // this will have to be replaced for new closed groups

View File

@ -592,7 +592,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
recipient.isLocalNumber -> getString(R.string.note_to_self) recipient.isLocalNumber -> getString(R.string.note_to_self)
else -> recipient.toShortString() else -> recipient.toShortString()
} }
@DimenRes val sizeID: Int = if (viewModel.recipient?.isClosedGroupRecipient == true) { @DimenRes val sizeID: Int = if (viewModel.recipient?.isLegacyClosedGroupRecipient == true) {
R.dimen.medium_profile_picture_size R.dimen.medium_profile_picture_size
} else { } else {
R.dimen.small_profile_picture_size R.dimen.small_profile_picture_size
@ -823,8 +823,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
} }
private fun showOrHideInputIfNeeded() { private fun showOrHideInputIfNeeded() {
val recipient = viewModel.recipient val recipient = viewModel.recipient ?: return
if (recipient != null && recipient.isClosedGroupRecipient) { if (recipient.isLegacyClosedGroupRecipient) {
val group = groupDb.getGroup(recipient.address.toGroupString()).orNull() val group = groupDb.getGroup(recipient.address.toGroupString()).orNull()
val isActive = (group?.isActive == true) val isActive = (group?.isActive == true)
binding?.inputBar?.showInput = isActive binding?.inputBar?.showInput = isActive
@ -1157,7 +1157,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
} }
override fun onClick(v: View?) { override fun onClick(v: View?) {
if (v === binding?.toolbarContent?.profilePictureView?.root) { if (v === binding?.toolbarContent?.profilePictureView) {
// open conversation settings // open conversation settings
conversationSettingsCallback.launch(viewModel.threadId) conversationSettingsCallback.launch(viewModel.threadId)
} }
@ -1197,8 +1197,9 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
} }
// TODO: don't need to allow new closed group check here, removed in new disappearing messages
override fun showExpiringMessagesDialog(thread: Recipient) { override fun showExpiringMessagesDialog(thread: Recipient) {
if (thread.isClosedGroupRecipient) { if (thread.isLegacyClosedGroupRecipient) {
val group = groupDb.getGroup(thread.address.toGroupString()).orNull() val group = groupDb.getGroup(thread.address.toGroupString()).orNull()
if (group?.isActive == false) { return } if (group?.isActive == false) { return }
} }

View File

@ -63,7 +63,7 @@ class DeleteOptionsBottomSheet : BottomSheetDialogFragment(), View.OnClickListen
binding.deleteForEveryoneTextView.text = binding.deleteForEveryoneTextView.text =
resources.getString(R.string.delete_message_for_me_and_recipient, contact) resources.getString(R.string.delete_message_for_me_and_recipient, contact)
} }
binding.deleteForEveryoneTextView.isVisible = !recipient.isClosedGroupRecipient binding.deleteForEveryoneTextView.isVisible = !recipient.isLegacyClosedGroupRecipient
binding.deleteForMeTextView.setOnClickListener(this) binding.deleteForMeTextView.setOnClickListener(this)
binding.deleteForEveryoneTextView.setOnClickListener(this) binding.deleteForEveryoneTextView.setOnClickListener(this)
binding.cancelTextView.setOnClickListener(this) binding.cancelTextView.setOnClickListener(this)

View File

@ -55,6 +55,7 @@ import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import network.loki.messenger.R import network.loki.messenger.R
import network.loki.messenger.databinding.ViewVisibleMessageContentBinding import network.loki.messenger.databinding.ViewVisibleMessageContentBinding
import org.session.libsession.database.StorageProtocol
import org.thoughtcrime.securesms.MediaPreviewActivity.getPreviewIntent import org.thoughtcrime.securesms.MediaPreviewActivity.getPreviewIntent
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.database.Storage import org.thoughtcrime.securesms.database.Storage

View File

@ -9,7 +9,6 @@ import android.text.style.StyleSpan
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import network.loki.messenger.R import network.loki.messenger.R
import network.loki.messenger.databinding.DialogDownloadBinding
import org.session.libsession.database.StorageProtocol import org.session.libsession.database.StorageProtocol
import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
@ -31,20 +30,21 @@ class AutoDownloadDialog(private val threadRecipient: Recipient,
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog {
val threadId = storage.getThreadId(threadRecipient) ?: run { val threadId = storage.getThreadId(threadRecipient) ?: run {
dismiss() dismiss()
return return@createSessionDialog
} }
val displayName = when { val displayName = when {
threadRecipient.isOpenGroupRecipient -> storage.getOpenGroup(threadId)?.name ?: "UNKNOWN" threadRecipient.isOpenGroupRecipient -> storage.getOpenGroup(threadId)?.name ?: "UNKNOWN"
threadRecipient.isClosedGroupRecipient -> storage.getGroup(threadRecipient.address.toGroupString())?.title ?: "UNKNOWN" threadRecipient.isLegacyClosedGroupRecipient -> storage.getGroup(threadRecipient.address.toGroupString())?.title ?: "UNKNOWN"
// TODO: threadRecipient.isClosedGroupRecipient -> storage.getLibSessionGroup(threadRecipient.address.serialize())?.groupName ?: "UNKNOWN" or something
else -> storage.getContactWithSessionID(threadRecipient.address.serialize())?.displayName(Contact.ContactContext.REGULAR) ?: "UNKNOWN" else -> storage.getContactWithSessionID(threadRecipient.address.serialize())?.displayName(Contact.ContactContext.REGULAR) ?: "UNKNOWN"
} }
title(resources.getString(R.string.dialog_auto_download_title)) title(resources.getString(R.string.dialog_auto_download_title))
val explanation = resources.getString(R.string.dialog_auto_download_explanation, displayName) val explanation = resources.getString(R.string.dialog_auto_download_explanation, displayName)
val spannable = SpannableStringBuilder(explanation) val spannable = SpannableStringBuilder(explanation)
val startIndex = explanation.indexOf(name) val startIndex = explanation.indexOf(displayName)
spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + name.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + displayName.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
text(spannable) text(spannable)
button(R.string.dialog_download_button_title, R.string.AccessibilityId_download_media) { button(R.string.dialog_download_button_title, R.string.AccessibilityId_download_media) {

View File

@ -63,7 +63,7 @@ object ConversationMenuHelper {
// Base menu (options that should always be present) // Base menu (options that should always be present)
inflater.inflate(R.menu.menu_conversation, menu) inflater.inflate(R.menu.menu_conversation, menu)
// Expiring messages // Expiring messages
if (!isOpenGroup && (thread.hasApprovedMe() || thread.isClosedGroupRecipient) && !thread.isBlocked) { if (!isOpenGroup && (thread.hasApprovedMe() || thread.isLegacyClosedGroupRecipient) && !thread.isBlocked) {
if (thread.expireMessages > 0) { if (thread.expireMessages > 0) {
inflater.inflate(R.menu.menu_conversation_expiration_on, menu) inflater.inflate(R.menu.menu_conversation_expiration_on, menu)
val item = menu.findItem(R.id.menu_expiring_messages) val item = menu.findItem(R.id.menu_expiring_messages)
@ -92,7 +92,7 @@ object ConversationMenuHelper {
} }
} }
// Closed group menu (options that should only be present in closed groups) // Closed group menu (options that should only be present in closed groups)
if (thread.isClosedGroupRecipient) { if (thread.isLegacyClosedGroupRecipient) {
inflater.inflate(R.menu.menu_conversation_closed_group, menu) inflater.inflate(R.menu.menu_conversation_closed_group, menu)
} }
// Open group menu // Open group menu
@ -280,7 +280,7 @@ object ConversationMenuHelper {
} }
private fun editClosedGroup(context: Context, thread: Recipient) { private fun editClosedGroup(context: Context, thread: Recipient) {
if (!thread.isClosedGroupRecipient) { return } if (!thread.isLegacyClosedGroupRecipient) { return }
val intent = Intent(context, EditClosedGroupActivity::class.java) val intent = Intent(context, EditClosedGroupActivity::class.java)
val groupID: String = thread.address.toGroupString() val groupID: String = thread.address.toGroupString()
intent.putExtra(groupIDKey, groupID) intent.putExtra(groupIDKey, groupID)
@ -288,7 +288,7 @@ object ConversationMenuHelper {
} }
private fun leaveClosedGroup(context: Context, thread: Recipient) { private fun leaveClosedGroup(context: Context, thread: Recipient) {
if (!thread.isClosedGroupRecipient) { return } if (!thread.isLegacyClosedGroupRecipient) { return }
val group = DatabaseComponent.get(context).groupDatabase().getGroup(thread.address.toGroupString()).orNull() val group = DatabaseComponent.get(context).groupDatabase().getGroup(thread.address.toGroupString()).orNull()
val admins = group.admins val admins = group.admins

View File

@ -64,7 +64,6 @@ class VisibleMessageContentView : ConstraintLayout {
glide: GlideRequests = GlideApp.with(this), glide: GlideRequests = GlideApp.with(this),
thread: Recipient, thread: Recipient,
searchQuery: String? = null, searchQuery: String? = null,
contactIsTrusted: Boolean = true,
onAttachmentNeedsDownload: (Long, Long) -> Unit, onAttachmentNeedsDownload: (Long, Long) -> Unit,
suppressThumbnails: Boolean = false suppressThumbnails: Boolean = false
) { ) {

View File

@ -11,23 +11,29 @@ object MentionManagerUtilities {
fun populateUserPublicKeyCacheIfNeeded(threadID: Long, context: Context) { fun populateUserPublicKeyCacheIfNeeded(threadID: Long, context: Context) {
val result = mutableSetOf<String>() val result = mutableSetOf<String>()
val recipient = DatabaseComponent.get(context).threadDatabase().getRecipientForThreadId(threadID) ?: return val recipient = DatabaseComponent.get(context).threadDatabase().getRecipientForThreadId(threadID) ?: return
if (recipient.address.isClosedGroup) { when {
val members = DatabaseComponent.get(context).groupDatabase().getGroupMembers(recipient.address.toGroupString(), false).map { it.address.serialize() } recipient.address.isLegacyClosedGroup -> {
result.addAll(members) val members = DatabaseComponent.get(context).groupDatabase().getGroupMembers(recipient.address.toGroupString(), false).map { it.address.serialize() }
} else { result.addAll(members)
val messageDatabase = DatabaseComponent.get(context).mmsSmsDatabase() }
val reader = messageDatabase.readerFor(messageDatabase.getConversation(threadID, true, 0, 200)) recipient.address.isClosedGroup -> {
var record: MessageRecord? = reader.next TODO("get members from libsession via storage")
while (record != null) { }
result.add(record.individualRecipient.address.serialize()) recipient.address.isOpenGroup -> {
try { val messageDatabase = DatabaseComponent.get(context).mmsSmsDatabase()
record = reader.next val reader = messageDatabase.readerFor(messageDatabase.getConversation(threadID, true, 0, 200))
} catch (exception: Exception) { var record: MessageRecord? = reader.next
record = null while (record != null) {
} result.add(record.individualRecipient.address.serialize())
try {
record = reader.next
} catch (exception: Exception) {
record = null
}
}
reader.close()
result.add(TextSecurePreferences.getLocalNumber(context)!!)
} }
reader.close()
result.add(TextSecurePreferences.getLocalNumber(context)!!)
} }
MentionsManager.userPublicKeyCache[threadID] = result MentionsManager.userPublicKeyCache[threadID] = result
} }

View File

@ -932,7 +932,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
cursor?.close() cursor?.close()
} }
val threadDb = get(context).threadDatabase() val threadDb = get(context).threadDatabase()
threadDb.update(threadId, false) threadDb.update(threadId, false, false)
notifyConversationListeners(threadId) notifyConversationListeners(threadId)
notifyStickerListeners() notifyStickerListeners()
notifyStickerPackListeners() notifyStickerPackListeners()
@ -959,7 +959,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
cursor?.close() cursor?.close()
} }
val threadDb = get(context).threadDatabase() val threadDb = get(context).threadDatabase()
threadDb.update(threadId, false) threadDb.update(threadId, false, true)
notifyConversationListeners(threadId) notifyConversationListeners(threadId)
notifyStickerListeners() notifyStickerListeners()
notifyStickerPackListeners() notifyStickerPackListeners()

View File

@ -5,6 +5,7 @@ import android.net.Uri
import network.loki.messenger.libsession_util.ConfigBase import network.loki.messenger.libsession_util.ConfigBase
import network.loki.messenger.libsession_util.ConfigBase.Companion.PRIORITY_HIDDEN import network.loki.messenger.libsession_util.ConfigBase.Companion.PRIORITY_HIDDEN
import network.loki.messenger.libsession_util.ConfigBase.Companion.PRIORITY_PINNED import network.loki.messenger.libsession_util.ConfigBase.Companion.PRIORITY_PINNED
import network.loki.messenger.libsession_util.ConfigBase.Companion.PRIORITY_VISIBLE
import network.loki.messenger.libsession_util.Contacts import network.loki.messenger.libsession_util.Contacts
import network.loki.messenger.libsession_util.ConversationVolatileConfig import network.loki.messenger.libsession_util.ConversationVolatileConfig
import network.loki.messenger.libsession_util.UserGroupsConfig import network.loki.messenger.libsession_util.UserGroupsConfig
@ -101,20 +102,26 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co
val volatile = configFactory.convoVolatile ?: return val volatile = configFactory.convoVolatile ?: return
if (address.isGroup) { if (address.isGroup) {
val groups = configFactory.userGroups ?: return val groups = configFactory.userGroups ?: return
if (address.isClosedGroup) { when {
val sessionId = GroupUtil.doubleDecodeGroupId(address.serialize()) address.isLegacyClosedGroup -> {
val closedGroup = getGroup(address.toGroupString()) val sessionId = GroupUtil.doubleDecodeGroupId(address.serialize())
if (closedGroup != null && closedGroup.isActive) { val closedGroup = getGroup(address.toGroupString())
val legacyGroup = groups.getOrConstructLegacyGroupInfo(sessionId) if (closedGroup != null && closedGroup.isActive) {
groups.set(legacyGroup) val legacyGroup = groups.getOrConstructLegacyGroupInfo(sessionId)
val newVolatileParams = volatile.getOrConstructLegacyGroup(sessionId).copy( groups.set(legacyGroup)
lastRead = SnodeAPI.nowWithOffset, val newVolatileParams = volatile.getOrConstructLegacyGroup(sessionId).copy(
) lastRead = SnodeAPI.nowWithOffset,
volatile.set(newVolatileParams) )
volatile.set(newVolatileParams)
}
}
address.isClosedGroup -> {
Log.w("Loki", "Thread created called for new closed group address, not adding any extra information")
}
address.isOpenGroup -> {
// these should be added on the group join / group info fetch
Log.w("Loki", "Thread created called for open group address, not adding any extra information")
} }
} else if (address.isOpenGroup) {
// these should be added on the group join / group info fetch
Log.w("Loki", "Thread created called for open group address, not adding any extra information")
} }
} else if (address.isContact) { } else if (address.isContact) {
// non-standard contact prefixes: 15, 00 etc shouldn't be stored in config // non-standard contact prefixes: 15, 00 etc shouldn't be stored in config
@ -123,11 +130,11 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co
if (getUserPublicKey() != address.serialize()) { if (getUserPublicKey() != address.serialize()) {
val contacts = configFactory.contacts ?: return val contacts = configFactory.contacts ?: return
contacts.upsertContact(address.serialize()) { contacts.upsertContact(address.serialize()) {
priority = ConfigBase.PRIORITY_VISIBLE priority = PRIORITY_VISIBLE
} }
} else { } else {
val userProfile = configFactory.user ?: return val userProfile = configFactory.user ?: return
userProfile.setNtsPriority(ConfigBase.PRIORITY_VISIBLE) userProfile.setNtsPriority(PRIORITY_VISIBLE)
DatabaseComponent.get(context).threadDatabase().setHasSent(threadId, true) DatabaseComponent.get(context).threadDatabase().setHasSent(threadId, true)
} }
val newVolatileParams = volatile.getOrConstructOneToOne(address.serialize()) val newVolatileParams = volatile.getOrConstructOneToOne(address.serialize())
@ -241,7 +248,8 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co
configFactory.convoVolatile?.let { config -> configFactory.convoVolatile?.let { config ->
val convo = when { val convo = when {
// recipient closed group // recipient closed group
recipient.isClosedGroupRecipient -> config.getOrConstructLegacyGroup(GroupUtil.doubleDecodeGroupId(recipient.address.serialize())) recipient.isLegacyClosedGroupRecipient -> config.getOrConstructLegacyGroup(GroupUtil.doubleDecodeGroupId(recipient.address.serialize()))
recipient.isClosedGroupRecipient -> config.getOrConstructGroup(recipient.address.serialize())
// recipient is open group // recipient is open group
recipient.isOpenGroupRecipient -> { recipient.isOpenGroupRecipient -> {
val openGroupJoinUrl = getOpenGroup(threadId)?.joinURL ?: return val openGroupJoinUrl = getOpenGroup(threadId)?.joinURL ?: return
@ -848,7 +856,7 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co
sessionId = groupPublicKey, sessionId = groupPublicKey,
name = name, name = name,
members = members, members = members,
priority = ConfigBase.PRIORITY_VISIBLE, priority = PRIORITY_VISIBLE,
encPubKey = (encryptionKeyPair.publicKey as DjbECPublicKey).publicKey, // 'serialize()' inserts an extra byte encPubKey = (encryptionKeyPair.publicKey as DjbECPublicKey).publicKey, // 'serialize()' inserts an extra byte
encSecKey = encryptionKeyPair.privateKey.serialize(), encSecKey = encryptionKeyPair.privateKey.serialize(),
disappearingTimer = 0L, disappearingTimer = 0L,
@ -884,7 +892,7 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co
members = membersMap, members = membersMap,
encPubKey = (latestKeyPair.publicKey as DjbECPublicKey).publicKey, // 'serialize()' inserts an extra byte encPubKey = (latestKeyPair.publicKey as DjbECPublicKey).publicKey, // 'serialize()' inserts an extra byte
encSecKey = latestKeyPair.privateKey.serialize(), encSecKey = latestKeyPair.privateKey.serialize(),
priority = if (isPinned(threadID)) PRIORITY_PINNED else ConfigBase.PRIORITY_VISIBLE, priority = if (isPinned(threadID)) PRIORITY_PINNED else PRIORITY_VISIBLE,
disappearingTimer = recipientSettings.expireMessages.toLong(), disappearingTimer = recipientSettings.expireMessages.toLong(),
joinedAt = (existingGroup.formationTimestamp / 1000L) joinedAt = (existingGroup.formationTimestamp / 1000L)
) )
@ -1264,27 +1272,36 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co
val threadRecipient = getRecipientForThread(threadID) ?: return val threadRecipient = getRecipientForThread(threadID) ?: return
if (threadRecipient.isLocalNumber) { if (threadRecipient.isLocalNumber) {
val user = configFactory.user ?: return val user = configFactory.user ?: return
user.setNtsPriority(if (isPinned) PRIORITY_PINNED else ConfigBase.PRIORITY_VISIBLE) user.setNtsPriority(if (isPinned) PRIORITY_PINNED else PRIORITY_VISIBLE)
} else if (threadRecipient.isContactRecipient) { } else if (threadRecipient.isContactRecipient) {
val contacts = configFactory.contacts ?: return val contacts = configFactory.contacts ?: return
contacts.upsertContact(threadRecipient.address.serialize()) { contacts.upsertContact(threadRecipient.address.serialize()) {
priority = if (isPinned) PRIORITY_PINNED else ConfigBase.PRIORITY_VISIBLE priority = if (isPinned) PRIORITY_PINNED else PRIORITY_VISIBLE
} }
} else if (threadRecipient.isGroupRecipient) { } else if (threadRecipient.isGroupRecipient) {
val groups = configFactory.userGroups ?: return val groups = configFactory.userGroups ?: return
if (threadRecipient.isClosedGroupRecipient) { when {
val sessionId = GroupUtil.doubleDecodeGroupId(threadRecipient.address.serialize()) threadRecipient.isLegacyClosedGroupRecipient -> {
val newGroupInfo = groups.getOrConstructLegacyGroupInfo(sessionId).copy ( val sessionId = GroupUtil.doubleDecodeGroupId(threadRecipient.address.serialize())
priority = if (isPinned) PRIORITY_PINNED else ConfigBase.PRIORITY_VISIBLE val newGroupInfo = groups.getOrConstructLegacyGroupInfo(sessionId).copy (
) priority = if (isPinned) PRIORITY_PINNED else PRIORITY_VISIBLE
groups.set(newGroupInfo) )
} else if (threadRecipient.isOpenGroupRecipient) { groups.set(newGroupInfo)
val openGroup = getOpenGroup(threadID) ?: return }
val (baseUrl, room, pubKeyHex) = BaseCommunityInfo.parseFullUrl(openGroup.joinURL) ?: return threadRecipient.isClosedGroupRecipient -> {
val newGroupInfo = groups.getOrConstructCommunityInfo(baseUrl, room, Hex.toStringCondensed(pubKeyHex)).copy ( val newGroupInfo = groups.getGroupInfo(threadRecipient.address.serialize()).copy (
priority = if (isPinned) PRIORITY_PINNED else ConfigBase.PRIORITY_VISIBLE priority = if (isPinned) PRIORITY_PINNED else PRIORITY_VISIBLE
) )
groups.set(newGroupInfo) groups.set(newGroupInfo)
}
threadRecipient.isOpenGroupRecipient -> {
val openGroup = getOpenGroup(threadID) ?: return
val (baseUrl, room, pubKeyHex) = BaseCommunityInfo.parseFullUrl(openGroup.joinURL) ?: return
val newGroupInfo = groups.getOrConstructCommunityInfo(baseUrl, room, Hex.toStringCondensed(pubKeyHex)).copy (
priority = if (isPinned) PRIORITY_PINNED else PRIORITY_VISIBLE
)
groups.set(newGroupInfo)
}
} }
} }
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context) ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context)
@ -1341,7 +1358,7 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co
} else { } else {
smsDb.deleteMessagesFrom(threadID, fromUser.serialize()) smsDb.deleteMessagesFrom(threadID, fromUser.serialize())
mmsDb.deleteMessagesFrom(threadID, fromUser.serialize()) mmsDb.deleteMessagesFrom(threadID, fromUser.serialize())
threadDb.update(threadID, false) threadDb.update(threadID, false, true)
} }
return true return true
} }

View File

@ -17,7 +17,7 @@
*/ */
package org.thoughtcrime.securesms.database; package org.thoughtcrime.securesms.database;
import static org.session.libsession.utilities.GroupUtil.CLOSED_GROUP_PREFIX; import static org.session.libsession.utilities.GroupUtil.LEGACY_CLOSED_GROUP_PREFIX;
import static org.session.libsession.utilities.GroupUtil.OPEN_GROUP_PREFIX; import static org.session.libsession.utilities.GroupUtil.OPEN_GROUP_PREFIX;
import static org.thoughtcrime.securesms.database.GroupDatabase.GROUP_ID; import static org.thoughtcrime.securesms.database.GroupDatabase.GROUP_ID;
@ -503,7 +503,7 @@ public class ThreadDatabase extends Database {
} }
public Cursor getApprovedConversationList() { public Cursor getApprovedConversationList() {
String where = "((" + HAS_SENT + " = 1 OR " + RecipientDatabase.APPROVED + " = 1 OR "+ GroupDatabase.TABLE_NAME +"."+GROUP_ID+" LIKE '"+CLOSED_GROUP_PREFIX+"%') OR " + GroupDatabase.TABLE_NAME + "." + GROUP_ID + " LIKE '" + OPEN_GROUP_PREFIX + "%') " + String where = "((" + HAS_SENT + " = 1 OR " + RecipientDatabase.APPROVED + " = 1 OR "+ GroupDatabase.TABLE_NAME +"."+GROUP_ID+" LIKE '"+ LEGACY_CLOSED_GROUP_PREFIX +"%') OR " + GroupDatabase.TABLE_NAME + "." + GROUP_ID + " LIKE '" + OPEN_GROUP_PREFIX + "%') " +
"AND " + ARCHIVED + " = 0 "; "AND " + ARCHIVED + " = 0 ";
return getConversationList(where); return getConversationList(where);
} }

View File

@ -0,0 +1,17 @@
package org.thoughtcrime.securesms.dependencies
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.session.libsession.database.StorageProtocol
import org.thoughtcrime.securesms.database.Storage
@Module
@InstallIn(SingletonComponent::class)
abstract class DatabaseBindings {
@Binds
abstract fun bindStorageProtocol(storage: Storage): StorageProtocol
}

View File

@ -42,7 +42,7 @@ interface DatabaseComponent {
fun sessionContactDatabase(): SessionContactDatabase fun sessionContactDatabase(): SessionContactDatabase
fun reactionDatabase(): ReactionDatabase fun reactionDatabase(): ReactionDatabase
fun emojiSearchDatabase(): EmojiSearchDatabase fun emojiSearchDatabase(): EmojiSearchDatabase
fun storage(): StorageProtocol fun storage(): Storage
fun attachmentProvider(): MessageDataProvider fun attachmentProvider(): MessageDataProvider
fun blindedIdMappingDatabase(): BlindedIdMappingDatabase fun blindedIdMappingDatabase(): BlindedIdMappingDatabase
fun groupMemberDatabase(): GroupMemberDatabase fun groupMemberDatabase(): GroupMemberDatabase

View File

@ -273,13 +273,13 @@ object ConfigurationMessageUtilities {
@JvmField @JvmField
val DELETE_INACTIVE_GROUPS: String = """ val DELETE_INACTIVE_GROUPS: String = """
DELETE FROM ${GroupDatabase.TABLE_NAME} WHERE ${GroupDatabase.GROUP_ID} IN (SELECT ${ThreadDatabase.ADDRESS} FROM ${ThreadDatabase.TABLE_NAME} WHERE ${ThreadDatabase.MESSAGE_COUNT} <= 0 AND ${ThreadDatabase.ADDRESS} LIKE '${GroupUtil.CLOSED_GROUP_PREFIX}%'); DELETE FROM ${GroupDatabase.TABLE_NAME} WHERE ${GroupDatabase.GROUP_ID} IN (SELECT ${ThreadDatabase.ADDRESS} FROM ${ThreadDatabase.TABLE_NAME} WHERE ${ThreadDatabase.MESSAGE_COUNT} <= 0 AND ${ThreadDatabase.ADDRESS} LIKE '${GroupUtil.LEGACY_CLOSED_GROUP_PREFIX}%');
DELETE FROM ${ThreadDatabase.TABLE_NAME} WHERE ${ThreadDatabase.ADDRESS} IN (SELECT ${ThreadDatabase.ADDRESS} FROM ${ThreadDatabase.TABLE_NAME} WHERE ${ThreadDatabase.MESSAGE_COUNT} <= 0 AND ${ThreadDatabase.ADDRESS} LIKE '${GroupUtil.CLOSED_GROUP_PREFIX}%'); DELETE FROM ${ThreadDatabase.TABLE_NAME} WHERE ${ThreadDatabase.ADDRESS} IN (SELECT ${ThreadDatabase.ADDRESS} FROM ${ThreadDatabase.TABLE_NAME} WHERE ${ThreadDatabase.MESSAGE_COUNT} <= 0 AND ${ThreadDatabase.ADDRESS} LIKE '${GroupUtil.LEGACY_CLOSED_GROUP_PREFIX}%');
""".trimIndent() """.trimIndent()
@JvmField @JvmField
val DELETE_INACTIVE_ONE_TO_ONES: String = """ val DELETE_INACTIVE_ONE_TO_ONES: String = """
DELETE FROM ${ThreadDatabase.TABLE_NAME} WHERE ${ThreadDatabase.MESSAGE_COUNT} <= 0 AND ${ThreadDatabase.ADDRESS} NOT LIKE '${GroupUtil.CLOSED_GROUP_PREFIX}%' AND ${ThreadDatabase.ADDRESS} NOT LIKE '${GroupUtil.OPEN_GROUP_PREFIX}%' AND ${ThreadDatabase.ADDRESS} NOT LIKE '${GroupUtil.OPEN_GROUP_INBOX_PREFIX}%'; DELETE FROM ${ThreadDatabase.TABLE_NAME} WHERE ${ThreadDatabase.MESSAGE_COUNT} <= 0 AND ${ThreadDatabase.ADDRESS} NOT LIKE '${GroupUtil.LEGACY_CLOSED_GROUP_PREFIX}%' AND ${ThreadDatabase.ADDRESS} NOT LIKE '${GroupUtil.OPEN_GROUP_PREFIX}%' AND ${ThreadDatabase.ADDRESS} NOT LIKE '${GroupUtil.OPEN_GROUP_INBOX_PREFIX}%';
""".trimIndent() """.trimIndent()
} }

View File

@ -157,14 +157,14 @@ data class ConfigurationSyncJob(val destination: Destination): Job {
fun Destination.destinationPublicKey(): String = when (this) { fun Destination.destinationPublicKey(): String = when (this) {
is Destination.Contact -> publicKey is Destination.Contact -> publicKey
is Destination.ClosedGroup -> groupPublicKey is Destination.ClosedGroup -> publicKey
else -> throw NullPointerException("Not public key for this destination") else -> throw NullPointerException("Not public key for this destination")
} }
override fun serialize(): Data { override fun serialize(): Data {
val (type, address) = when (destination) { val (type, address) = when (destination) {
is Destination.Contact -> CONTACT_TYPE to destination.publicKey is Destination.Contact -> CONTACT_TYPE to destination.publicKey
is Destination.ClosedGroup -> GROUP_TYPE to destination.groupPublicKey is Destination.ClosedGroup -> GROUP_TYPE to destination.publicKey
else -> return Data.EMPTY else -> return Data.EMPTY
} }
return Data.Builder() return Data.Builder()

View File

@ -10,12 +10,15 @@ sealed class Destination {
data class Contact(var publicKey: String) : Destination() { data class Contact(var publicKey: String) : Destination() {
internal constructor(): this("") internal constructor(): this("")
} }
data class ClosedGroup(var groupPublicKey: String) : Destination() { data class LegacyClosedGroup(var groupPublicKey: String) : Destination() {
internal constructor(): this("") internal constructor(): this("")
} }
data class LegacyOpenGroup(var roomToken: String, var server: String) : Destination() { data class LegacyOpenGroup(var roomToken: String, var server: String) : Destination() {
internal constructor(): this("", "") internal constructor(): this("", "")
} }
data class ClosedGroup(var publicKey: String): Destination() {
internal constructor(): this("")
}
class OpenGroup( class OpenGroup(
var roomToken: String = "", var roomToken: String = "",
@ -38,10 +41,10 @@ sealed class Destination {
address.isContact -> { address.isContact -> {
Contact(address.contactIdentifier()) Contact(address.contactIdentifier())
} }
address.isClosedGroup -> { address.isLegacyClosedGroup -> {
val groupID = address.toGroupString() val groupID = address.toGroupString()
val groupPublicKey = GroupUtil.doubleDecodeGroupID(groupID).toHexString() val groupPublicKey = GroupUtil.doubleDecodeGroupID(groupID).toHexString()
ClosedGroup(groupPublicKey) LegacyClosedGroup(groupPublicKey)
} }
address.isOpenGroup -> { address.isOpenGroup -> {
val storage = MessagingModuleConfiguration.shared.storage val storage = MessagingModuleConfiguration.shared.storage

View File

@ -124,7 +124,7 @@ class ConfigurationMessage(var closedGroups: List<ClosedGroup>, var openGroups:
val profileKey = ProfileKeyUtil.getProfileKey(context) val profileKey = ProfileKeyUtil.getProfileKey(context)
val groups = storage.getAllGroups(includeInactive = false) val groups = storage.getAllGroups(includeInactive = false)
for (group in groups) { for (group in groups) {
if (group.isClosedGroup && group.isActive) { if (group.isLegacyClosedGroup && group.isActive) {
if (!group.members.contains(Address.fromSerialized(storage.getUserPublicKey()!!))) continue if (!group.members.contains(Address.fromSerialized(storage.getUserPublicKey()!!))) continue
val groupPublicKey = GroupUtil.doubleDecodeGroupID(group.encodedId).toHexString() val groupPublicKey = GroupUtil.doubleDecodeGroupID(group.encodedId).toHexString()
val encryptionKeyPair = storage.getLatestClosedGroupEncryptionKeyPair(groupPublicKey) ?: continue val encryptionKeyPair = storage.getLatestClosedGroupEncryptionKeyPair(groupPublicKey) ?: continue

View File

@ -87,7 +87,7 @@ object MessageSender {
when (destination) { when (destination) {
is Destination.Contact -> message.recipient = destination.publicKey is Destination.Contact -> message.recipient = destination.publicKey
is Destination.ClosedGroup -> message.recipient = destination.groupPublicKey is Destination.LegacyClosedGroup -> message.recipient = destination.groupPublicKey
else -> throw IllegalStateException("Destination should not be an open group.") else -> throw IllegalStateException("Destination should not be an open group.")
} }
@ -126,7 +126,7 @@ object MessageSender {
// Encrypt the serialized protobuf // Encrypt the serialized protobuf
val ciphertext = when (destination) { val ciphertext = when (destination) {
is Destination.Contact -> MessageEncrypter.encrypt(plaintext, destination.publicKey) is Destination.Contact -> MessageEncrypter.encrypt(plaintext, destination.publicKey)
is Destination.ClosedGroup -> { is Destination.LegacyClosedGroup -> {
val encryptionKeyPair = val encryptionKeyPair =
MessagingModuleConfiguration.shared.storage.getLatestClosedGroupEncryptionKeyPair( MessagingModuleConfiguration.shared.storage.getLatestClosedGroupEncryptionKeyPair(
destination.groupPublicKey destination.groupPublicKey
@ -143,7 +143,7 @@ object MessageSender {
kind = SignalServiceProtos.Envelope.Type.SESSION_MESSAGE kind = SignalServiceProtos.Envelope.Type.SESSION_MESSAGE
senderPublicKey = "" senderPublicKey = ""
} }
is Destination.ClosedGroup -> { is Destination.LegacyClosedGroup -> {
kind = SignalServiceProtos.Envelope.Type.CLOSED_GROUP_MESSAGE kind = SignalServiceProtos.Envelope.Type.CLOSED_GROUP_MESSAGE
senderPublicKey = destination.groupPublicKey senderPublicKey = destination.groupPublicKey
} }
@ -183,9 +183,9 @@ object MessageSender {
// TODO: this might change in future for config messages // TODO: this might change in future for config messages
val forkInfo = SnodeAPI.forkInfo val forkInfo = SnodeAPI.forkInfo
val namespaces: List<Int> = when { val namespaces: List<Int> = when {
destination is Destination.ClosedGroup destination is Destination.LegacyClosedGroup
&& forkInfo.defaultRequiresAuth() -> listOf(Namespace.UNAUTHENTICATED_CLOSED_GROUP) && forkInfo.defaultRequiresAuth() -> listOf(Namespace.UNAUTHENTICATED_CLOSED_GROUP)
destination is Destination.ClosedGroup destination is Destination.LegacyClosedGroup
&& forkInfo.hasNamespaces() -> listOf(Namespace.UNAUTHENTICATED_CLOSED_GROUP, Namespace.DEFAULT) && forkInfo.hasNamespaces() -> listOf(Namespace.UNAUTHENTICATED_CLOSED_GROUP, Namespace.DEFAULT)
else -> listOf(Namespace.DEFAULT) else -> listOf(Namespace.DEFAULT)
} }

View File

@ -20,9 +20,9 @@ class Address private constructor(address: String) : Parcelable, Comparable<Addr
constructor(`in`: Parcel) : this(`in`.readString()!!) {} constructor(`in`: Parcel) : this(`in`.readString()!!) {}
val isGroup: Boolean val isGroup: Boolean
get() = GroupUtil.isEncodedGroup(address) get() = GroupUtil.isEncodedGroup(address) || address.startsWith(IdPrefix.GROUP.value)
val isClosedGroup: Boolean val isLegacyClosedGroup: Boolean
get() = GroupUtil.isClosedGroup(address) get() = GroupUtil.isLegacyClosedGroup(address)
val isOpenGroup: Boolean val isOpenGroup: Boolean
get() = GroupUtil.isOpenGroup(address) get() = GroupUtil.isOpenGroup(address)
val isOpenGroupInbox: Boolean val isOpenGroupInbox: Boolean

View File

@ -23,8 +23,8 @@ class GroupRecord(
val isOpenGroup: Boolean val isOpenGroup: Boolean
get() = Address.fromSerialized(encodedId).isOpenGroup get() = Address.fromSerialized(encodedId).isOpenGroup
val isClosedGroup: Boolean val isLegacyClosedGroup: Boolean
get() = Address.fromSerialized(encodedId).isClosedGroup get() = Address.fromSerialized(encodedId).isLegacyClosedGroup
init { init {
if (!TextUtils.isEmpty(members)) { if (!TextUtils.isEmpty(members)) {

View File

@ -1,12 +1,11 @@
package org.session.libsession.utilities package org.session.libsession.utilities
import network.loki.messenger.libsession_util.util.GroupInfo
import org.session.libsignal.messages.SignalServiceGroup import org.session.libsignal.messages.SignalServiceGroup
import org.session.libsignal.utilities.Hex import org.session.libsignal.utilities.Hex
import java.io.IOException import java.io.IOException
object GroupUtil { object GroupUtil {
const val CLOSED_GROUP_PREFIX = "__textsecure_group__!" const val LEGACY_CLOSED_GROUP_PREFIX = "__textsecure_group__!"
const val OPEN_GROUP_PREFIX = "__loki_public_chat_group__!" const val OPEN_GROUP_PREFIX = "__loki_public_chat_group__!"
const val OPEN_GROUP_INBOX_PREFIX = "__open_group_inbox__!" const val OPEN_GROUP_INBOX_PREFIX = "__open_group_inbox__!"
@ -22,7 +21,7 @@ object GroupUtil {
@JvmStatic @JvmStatic
fun getEncodedClosedGroupID(groupID: ByteArray): String { fun getEncodedClosedGroupID(groupID: ByteArray): String {
return CLOSED_GROUP_PREFIX + Hex.toStringCondensed(groupID) return LEGACY_CLOSED_GROUP_PREFIX + Hex.toStringCondensed(groupID)
} }
@JvmStatic @JvmStatic
@ -61,7 +60,7 @@ object GroupUtil {
} }
fun isEncodedGroup(groupId: String): Boolean { fun isEncodedGroup(groupId: String): Boolean {
return groupId.startsWith(CLOSED_GROUP_PREFIX) || groupId.startsWith(OPEN_GROUP_PREFIX) return groupId.startsWith(LEGACY_CLOSED_GROUP_PREFIX) || groupId.startsWith(OPEN_GROUP_PREFIX)
} }
@JvmStatic @JvmStatic
@ -75,8 +74,8 @@ object GroupUtil {
} }
@JvmStatic @JvmStatic
fun isClosedGroup(groupId: String): Boolean { fun isLegacyClosedGroup(groupId: String): Boolean {
return groupId.startsWith(CLOSED_GROUP_PREFIX) return groupId.startsWith(LEGACY_CLOSED_GROUP_PREFIX)
} }
// NOTE: Signal group ID handling is weird. The ID is double encoded in the database, but not in a `GroupContext`. // NOTE: Signal group ID handling is weird. The ID is double encoded in the database, but not in a `GroupContext`.

View File

@ -455,10 +455,11 @@ public class Recipient implements RecipientModifiedListener {
return address.isOpenGroupInbox(); return address.isOpenGroupInbox();
} }
public boolean isClosedGroupRecipient() { public boolean isLegacyClosedGroupRecipient() {
return address.isClosedGroup(); return address.isLegacyClosedGroup();
} }
@Deprecated @Deprecated
public boolean isPushGroupRecipient() { public boolean isPushGroupRecipient() {
return address.isGroup(); return address.isGroup();