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.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
import android.os.Build;
import android.os.Bundle;
@ -34,9 +33,7 @@ import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.view.ActionMode;
import androidx.appcompat.widget.Toolbar;
@ -53,7 +50,6 @@ import androidx.viewpager.widget.ViewPager;
import com.google.android.material.tabs.TabLayout;
import org.session.libsession.database.StorageProtocol;
import org.session.libsession.messaging.MessagingModuleConfiguration;
import org.session.libsession.messaging.messages.control.DataExtractionNotification;
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.recipients.Recipient;
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.MediaDatabase;
import org.thoughtcrime.securesms.database.loaders.ThreadMediaLoader;

View File

@ -54,7 +54,7 @@ class ProfilePictureView @JvmOverloads constructor(
return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey
}
if (recipient.isClosedGroupRecipient) {
if (recipient.isLegacyClosedGroupRecipient) {
val members = DatabaseComponent.get(context).groupDatabase()
.getGroupMemberAddresses(recipient.address.toGroupString(), true)
.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> {
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)
binding = ActivityConversationSettingsBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.profilePictureView.root.glide = GlideApp.with(this)
updateRecipientDisplay()
binding.searchConversation.setOnClickListener(this)
binding.clearMessages.setOnClickListener(this)
@ -91,7 +90,7 @@ class ConversationSettingsActivity: PassphraseRequiredActionBarActivity(), View.
}
// Toggle group-specific settings
val areGroupOptionsVisible = recipient.isClosedGroupRecipient
val areGroupOptionsVisible = recipient.isClosedGroupRecipient || recipient.isLegacyClosedGroupRecipient
groupOptions.forEach { v ->
v.isVisible = areGroupOptionsVisible
}
@ -164,7 +163,7 @@ class ConversationSettingsActivity: PassphraseRequiredActionBarActivity(), View.
}
v === binding.editGroup -> {
val recipient = viewModel.recipient ?: return
if (!recipient.isClosedGroupRecipient) return
if (!recipient.isClosedGroupRecipient || !recipient.isLegacyClosedGroupRecipient) return
val intent = Intent(this, EditClosedGroupActivity::class.java)
val groupID: String = recipient.address.toGroupString()
intent.putExtra(EditClosedGroupActivity.groupIDKey, groupID)

View File

@ -18,11 +18,11 @@ class ConversationSettingsViewModel(
val recipient get() = storage.getRecipientForThread(threadId)
fun isPinned() = storage.isThreadPinned(threadId)
fun isPinned() = storage.isPinned(threadId)
fun togglePin() = viewModelScope.launch {
val isPinned = storage.isThreadPinned(threadId)
storage.setThreadPinned(threadId, !isPinned)
val isPinned = storage.isPinned(threadId)
storage.setPinned(threadId, !isPinned)
}
fun autoDownloadAttachments() = recipient?.let { recipient -> storage.shouldAutoDownloadAttachments(recipient) } ?: false
@ -32,7 +32,7 @@ class ConversationSettingsViewModel(
}
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 group = storage.getGroup(recipient.address.toGroupString())
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)
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
} else {
R.dimen.small_profile_picture_size
@ -823,8 +823,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
}
private fun showOrHideInputIfNeeded() {
val recipient = viewModel.recipient
if (recipient != null && recipient.isClosedGroupRecipient) {
val recipient = viewModel.recipient ?: return
if (recipient.isLegacyClosedGroupRecipient) {
val group = groupDb.getGroup(recipient.address.toGroupString()).orNull()
val isActive = (group?.isActive == true)
binding?.inputBar?.showInput = isActive
@ -1157,7 +1157,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
}
override fun onClick(v: View?) {
if (v === binding?.toolbarContent?.profilePictureView?.root) {
if (v === binding?.toolbarContent?.profilePictureView) {
// open conversation settings
conversationSettingsCallback.launch(viewModel.threadId)
}
@ -1197,8 +1197,9 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
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) {
if (thread.isClosedGroupRecipient) {
if (thread.isLegacyClosedGroupRecipient) {
val group = groupDb.getGroup(thread.address.toGroupString()).orNull()
if (group?.isActive == false) { return }
}

View File

@ -63,7 +63,7 @@ class DeleteOptionsBottomSheet : BottomSheetDialogFragment(), View.OnClickListen
binding.deleteForEveryoneTextView.text =
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.deleteForEveryoneTextView.setOnClickListener(this)
binding.cancelTextView.setOnClickListener(this)

View File

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

View File

@ -9,7 +9,6 @@ import android.text.style.StyleSpan
import androidx.fragment.app.DialogFragment
import dagger.hilt.android.AndroidEntryPoint
import network.loki.messenger.R
import network.loki.messenger.databinding.DialogDownloadBinding
import org.session.libsession.database.StorageProtocol
import org.session.libsession.messaging.contacts.Contact
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 {
val threadId = storage.getThreadId(threadRecipient) ?: run {
dismiss()
return
return@createSessionDialog
}
val displayName = when {
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"
}
title(resources.getString(R.string.dialog_auto_download_title))
val explanation = resources.getString(R.string.dialog_auto_download_explanation, displayName)
val spannable = SpannableStringBuilder(explanation)
val startIndex = explanation.indexOf(name)
spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + name.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
val startIndex = explanation.indexOf(displayName)
spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + displayName.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
text(spannable)
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)
inflater.inflate(R.menu.menu_conversation, menu)
// Expiring messages
if (!isOpenGroup && (thread.hasApprovedMe() || thread.isClosedGroupRecipient) && !thread.isBlocked) {
if (!isOpenGroup && (thread.hasApprovedMe() || thread.isLegacyClosedGroupRecipient) && !thread.isBlocked) {
if (thread.expireMessages > 0) {
inflater.inflate(R.menu.menu_conversation_expiration_on, menu)
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)
if (thread.isClosedGroupRecipient) {
if (thread.isLegacyClosedGroupRecipient) {
inflater.inflate(R.menu.menu_conversation_closed_group, menu)
}
// Open group menu
@ -280,7 +280,7 @@ object ConversationMenuHelper {
}
private fun editClosedGroup(context: Context, thread: Recipient) {
if (!thread.isClosedGroupRecipient) { return }
if (!thread.isLegacyClosedGroupRecipient) { return }
val intent = Intent(context, EditClosedGroupActivity::class.java)
val groupID: String = thread.address.toGroupString()
intent.putExtra(groupIDKey, groupID)
@ -288,7 +288,7 @@ object ConversationMenuHelper {
}
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 admins = group.admins

View File

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

View File

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

View File

@ -932,7 +932,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
cursor?.close()
}
val threadDb = get(context).threadDatabase()
threadDb.update(threadId, false)
threadDb.update(threadId, false, false)
notifyConversationListeners(threadId)
notifyStickerListeners()
notifyStickerPackListeners()
@ -959,7 +959,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
cursor?.close()
}
val threadDb = get(context).threadDatabase()
threadDb.update(threadId, false)
threadDb.update(threadId, false, true)
notifyConversationListeners(threadId)
notifyStickerListeners()
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.Companion.PRIORITY_HIDDEN
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.ConversationVolatileConfig
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
if (address.isGroup) {
val groups = configFactory.userGroups ?: return
if (address.isClosedGroup) {
val sessionId = GroupUtil.doubleDecodeGroupId(address.serialize())
val closedGroup = getGroup(address.toGroupString())
if (closedGroup != null && closedGroup.isActive) {
val legacyGroup = groups.getOrConstructLegacyGroupInfo(sessionId)
groups.set(legacyGroup)
val newVolatileParams = volatile.getOrConstructLegacyGroup(sessionId).copy(
lastRead = SnodeAPI.nowWithOffset,
)
volatile.set(newVolatileParams)
when {
address.isLegacyClosedGroup -> {
val sessionId = GroupUtil.doubleDecodeGroupId(address.serialize())
val closedGroup = getGroup(address.toGroupString())
if (closedGroup != null && closedGroup.isActive) {
val legacyGroup = groups.getOrConstructLegacyGroupInfo(sessionId)
groups.set(legacyGroup)
val newVolatileParams = volatile.getOrConstructLegacyGroup(sessionId).copy(
lastRead = SnodeAPI.nowWithOffset,
)
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) {
// 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()) {
val contacts = configFactory.contacts ?: return
contacts.upsertContact(address.serialize()) {
priority = ConfigBase.PRIORITY_VISIBLE
priority = PRIORITY_VISIBLE
}
} else {
val userProfile = configFactory.user ?: return
userProfile.setNtsPriority(ConfigBase.PRIORITY_VISIBLE)
userProfile.setNtsPriority(PRIORITY_VISIBLE)
DatabaseComponent.get(context).threadDatabase().setHasSent(threadId, true)
}
val newVolatileParams = volatile.getOrConstructOneToOne(address.serialize())
@ -241,7 +248,8 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co
configFactory.convoVolatile?.let { config ->
val convo = when {
// 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.isOpenGroupRecipient -> {
val openGroupJoinUrl = getOpenGroup(threadId)?.joinURL ?: return
@ -848,7 +856,7 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co
sessionId = groupPublicKey,
name = name,
members = members,
priority = ConfigBase.PRIORITY_VISIBLE,
priority = PRIORITY_VISIBLE,
encPubKey = (encryptionKeyPair.publicKey as DjbECPublicKey).publicKey, // 'serialize()' inserts an extra byte
encSecKey = encryptionKeyPair.privateKey.serialize(),
disappearingTimer = 0L,
@ -884,7 +892,7 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co
members = membersMap,
encPubKey = (latestKeyPair.publicKey as DjbECPublicKey).publicKey, // 'serialize()' inserts an extra byte
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(),
joinedAt = (existingGroup.formationTimestamp / 1000L)
)
@ -1264,27 +1272,36 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co
val threadRecipient = getRecipientForThread(threadID) ?: return
if (threadRecipient.isLocalNumber) {
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) {
val contacts = configFactory.contacts ?: return
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) {
val groups = configFactory.userGroups ?: return
if (threadRecipient.isClosedGroupRecipient) {
val sessionId = GroupUtil.doubleDecodeGroupId(threadRecipient.address.serialize())
val newGroupInfo = groups.getOrConstructLegacyGroupInfo(sessionId).copy (
priority = if (isPinned) PRIORITY_PINNED else ConfigBase.PRIORITY_VISIBLE
)
groups.set(newGroupInfo)
} else if (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 ConfigBase.PRIORITY_VISIBLE
)
groups.set(newGroupInfo)
when {
threadRecipient.isLegacyClosedGroupRecipient -> {
val sessionId = GroupUtil.doubleDecodeGroupId(threadRecipient.address.serialize())
val newGroupInfo = groups.getOrConstructLegacyGroupInfo(sessionId).copy (
priority = if (isPinned) PRIORITY_PINNED else PRIORITY_VISIBLE
)
groups.set(newGroupInfo)
}
threadRecipient.isClosedGroupRecipient -> {
val newGroupInfo = groups.getGroupInfo(threadRecipient.address.serialize()).copy (
priority = if (isPinned) PRIORITY_PINNED else PRIORITY_VISIBLE
)
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)
@ -1341,7 +1358,7 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co
} else {
smsDb.deleteMessagesFrom(threadID, fromUser.serialize())
mmsDb.deleteMessagesFrom(threadID, fromUser.serialize())
threadDb.update(threadID, false)
threadDb.update(threadID, false, true)
}
return true
}

View File

@ -17,7 +17,7 @@
*/
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.thoughtcrime.securesms.database.GroupDatabase.GROUP_ID;
@ -503,7 +503,7 @@ public class ThreadDatabase extends Database {
}
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 ";
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 reactionDatabase(): ReactionDatabase
fun emojiSearchDatabase(): EmojiSearchDatabase
fun storage(): StorageProtocol
fun storage(): Storage
fun attachmentProvider(): MessageDataProvider
fun blindedIdMappingDatabase(): BlindedIdMappingDatabase
fun groupMemberDatabase(): GroupMemberDatabase

View File

@ -273,13 +273,13 @@ object ConfigurationMessageUtilities {
@JvmField
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 ${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 ${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.LEGACY_CLOSED_GROUP_PREFIX}%');
""".trimIndent()
@JvmField
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()
}

View File

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

View File

@ -10,12 +10,15 @@ sealed class Destination {
data class Contact(var publicKey: String) : Destination() {
internal constructor(): this("")
}
data class ClosedGroup(var groupPublicKey: String) : Destination() {
data class LegacyClosedGroup(var groupPublicKey: String) : Destination() {
internal constructor(): this("")
}
data class LegacyOpenGroup(var roomToken: String, var server: String) : Destination() {
internal constructor(): this("", "")
}
data class ClosedGroup(var publicKey: String): Destination() {
internal constructor(): this("")
}
class OpenGroup(
var roomToken: String = "",
@ -38,10 +41,10 @@ sealed class Destination {
address.isContact -> {
Contact(address.contactIdentifier())
}
address.isClosedGroup -> {
address.isLegacyClosedGroup -> {
val groupID = address.toGroupString()
val groupPublicKey = GroupUtil.doubleDecodeGroupID(groupID).toHexString()
ClosedGroup(groupPublicKey)
LegacyClosedGroup(groupPublicKey)
}
address.isOpenGroup -> {
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 groups = storage.getAllGroups(includeInactive = false)
for (group in groups) {
if (group.isClosedGroup && group.isActive) {
if (group.isLegacyClosedGroup && group.isActive) {
if (!group.members.contains(Address.fromSerialized(storage.getUserPublicKey()!!))) continue
val groupPublicKey = GroupUtil.doubleDecodeGroupID(group.encodedId).toHexString()
val encryptionKeyPair = storage.getLatestClosedGroupEncryptionKeyPair(groupPublicKey) ?: continue

View File

@ -87,7 +87,7 @@ object MessageSender {
when (destination) {
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.")
}
@ -126,7 +126,7 @@ object MessageSender {
// Encrypt the serialized protobuf
val ciphertext = when (destination) {
is Destination.Contact -> MessageEncrypter.encrypt(plaintext, destination.publicKey)
is Destination.ClosedGroup -> {
is Destination.LegacyClosedGroup -> {
val encryptionKeyPair =
MessagingModuleConfiguration.shared.storage.getLatestClosedGroupEncryptionKeyPair(
destination.groupPublicKey
@ -143,7 +143,7 @@ object MessageSender {
kind = SignalServiceProtos.Envelope.Type.SESSION_MESSAGE
senderPublicKey = ""
}
is Destination.ClosedGroup -> {
is Destination.LegacyClosedGroup -> {
kind = SignalServiceProtos.Envelope.Type.CLOSED_GROUP_MESSAGE
senderPublicKey = destination.groupPublicKey
}
@ -183,9 +183,9 @@ object MessageSender {
// TODO: this might change in future for config messages
val forkInfo = SnodeAPI.forkInfo
val namespaces: List<Int> = when {
destination is Destination.ClosedGroup
destination is Destination.LegacyClosedGroup
&& forkInfo.defaultRequiresAuth() -> listOf(Namespace.UNAUTHENTICATED_CLOSED_GROUP)
destination is Destination.ClosedGroup
destination is Destination.LegacyClosedGroup
&& forkInfo.hasNamespaces() -> listOf(Namespace.UNAUTHENTICATED_CLOSED_GROUP, 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()!!) {}
val isGroup: Boolean
get() = GroupUtil.isEncodedGroup(address)
val isClosedGroup: Boolean
get() = GroupUtil.isClosedGroup(address)
get() = GroupUtil.isEncodedGroup(address) || address.startsWith(IdPrefix.GROUP.value)
val isLegacyClosedGroup: Boolean
get() = GroupUtil.isLegacyClosedGroup(address)
val isOpenGroup: Boolean
get() = GroupUtil.isOpenGroup(address)
val isOpenGroupInbox: Boolean

View File

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

View File

@ -1,12 +1,11 @@
package org.session.libsession.utilities
import network.loki.messenger.libsession_util.util.GroupInfo
import org.session.libsignal.messages.SignalServiceGroup
import org.session.libsignal.utilities.Hex
import java.io.IOException
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_INBOX_PREFIX = "__open_group_inbox__!"
@ -22,7 +21,7 @@ object GroupUtil {
@JvmStatic
fun getEncodedClosedGroupID(groupID: ByteArray): String {
return CLOSED_GROUP_PREFIX + Hex.toStringCondensed(groupID)
return LEGACY_CLOSED_GROUP_PREFIX + Hex.toStringCondensed(groupID)
}
@JvmStatic
@ -61,7 +60,7 @@ object GroupUtil {
}
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
@ -75,8 +74,8 @@ object GroupUtil {
}
@JvmStatic
fun isClosedGroup(groupId: String): Boolean {
return groupId.startsWith(CLOSED_GROUP_PREFIX)
fun isLegacyClosedGroup(groupId: String): Boolean {
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`.

View File

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