From 841bc69c3c150c30aa03839f2eff98a77b4b9556 Mon Sep 17 00:00:00 2001 From: SessionHero01 <180888785+SessionHero01@users.noreply.github.com> Date: Thu, 12 Sep 2024 16:40:19 +1000 Subject: [PATCH] Remove conversation settings and added back group operation --- app/src/main/AndroidManifest.xml | 7 - ...onversationNotificationSettingsActivity.kt | 59 --- ...ionNotificationSettingsActivityContract.kt | 16 - .../settings/ConversationSettingsActivity.kt | 269 ------------- .../ConversationSettingsActivityContract.kt | 24 -- .../settings/ConversationSettingsViewModel.kt | 104 ----- .../conversation/v2/ConversationActivityV2.kt | 32 +- .../v2/menus/ConversationMenuHelper.kt | 100 ++++- ...ity_conversation_notification_settings.xml | 73 ---- .../layout/activity_conversation_settings.xml | 375 ------------------ .../res/menu/menu_conversation_groups_v2.xml | 12 + .../menu_conversation_groups_v2_admin.xml | 12 + ...xml => menu_conversation_legacy_group.xml} | 0 13 files changed, 111 insertions(+), 972 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ConversationNotificationSettingsActivity.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ConversationNotificationSettingsActivityContract.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ConversationSettingsActivity.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ConversationSettingsActivityContract.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ConversationSettingsViewModel.kt delete mode 100644 app/src/main/res/layout/activity_conversation_notification_settings.xml delete mode 100644 app/src/main/res/layout/activity_conversation_settings.xml create mode 100644 app/src/main/res/menu/menu_conversation_groups_v2.xml create mode 100644 app/src/main/res/menu/menu_conversation_groups_v2_admin.xml rename app/src/main/res/menu/{menu_conversation_closed_group.xml => menu_conversation_legacy_group.xml} (100%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9687c01143..eeead1c3ce 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -243,13 +243,6 @@ android:name="android.support.PARENT_ACTIVITY" android:value="org.thoughtcrime.securesms.home.HomeActivity" /> - - () { - - override fun createIntent(context: Context, input: Long): Intent = - Intent(context, ConversationNotificationSettingsActivity::class.java).apply { - putExtra(ConversationActivityV2.THREAD_ID, input) - } - - override fun parseResult(resultCode: Int, intent: Intent?) { /* do nothing */ } -} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ConversationSettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ConversationSettingsActivity.kt deleted file mode 100644 index a44b3fc55d..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ConversationSettingsActivity.kt +++ /dev/null @@ -1,269 +0,0 @@ -package org.thoughtcrime.securesms.conversation.settings - -import android.content.Intent -import android.os.Bundle -import android.view.View -import androidx.activity.viewModels -import androidx.core.view.isVisible -import androidx.lifecycle.lifecycleScope -import com.squareup.phrase.Phrase -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.launch -import network.loki.messenger.R -import network.loki.messenger.databinding.ActivityConversationSettingsBinding -import org.session.libsession.messaging.sending_receiving.MessageSender -import org.session.libsession.utilities.Address -import org.session.libsession.utilities.GroupUtil -import org.session.libsession.utilities.StringSubstitutionConstants.GROUP_NAME_KEY -import org.session.libsession.utilities.StringSubstitutionConstants.NAME_KEY -import org.session.libsession.utilities.TextSecurePreferences -import org.session.libsignal.utilities.Log -import org.session.libsignal.utilities.toHexString -import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity -import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 -import org.thoughtcrime.securesms.database.GroupDatabase -import org.thoughtcrime.securesms.database.LokiThreadDatabase -import org.thoughtcrime.securesms.database.ThreadDatabase -import org.thoughtcrime.securesms.groups.EditGroupActivity -import org.thoughtcrime.securesms.groups.EditLegacyGroupActivity -import org.thoughtcrime.securesms.media.MediaOverviewActivity -import org.thoughtcrime.securesms.showSessionDialog -import java.io.IOException -import javax.inject.Inject - -@AndroidEntryPoint -class ConversationSettingsActivity: PassphraseRequiredActionBarActivity(), View.OnClickListener { - - companion object { - // used to trigger displaying conversation search in calling parent activity - const val RESULT_SEARCH = 22 - } - - lateinit var binding: ActivityConversationSettingsBinding - - private val groupOptions: List - get() = with(binding) { - listOf( - groupMembers, - groupMembersDivider.root, - editGroup, - editGroupDivider.root, - leaveGroup, - leaveGroupDivider.root - ) - } - - @Inject lateinit var threadDb: ThreadDatabase - @Inject lateinit var groupDb: GroupDatabase - @Inject lateinit var lokiThreadDb: LokiThreadDatabase - @Inject lateinit var viewModelFactory: ConversationSettingsViewModel.AssistedFactory - val viewModel: ConversationSettingsViewModel by viewModels { - val threadId = intent.getLongExtra(ConversationActivityV2.THREAD_ID, -1L) - if (threadId == -1L) { - finish() - } - viewModelFactory.create(threadId) - } - - private val notificationActivityCallback = registerForActivityResult(ConversationNotificationSettingsActivityContract()) { - updateRecipientDisplay() - } - - override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) { - super.onCreate(savedInstanceState, ready) - binding = ActivityConversationSettingsBinding.inflate(layoutInflater) - setContentView(binding.root) - updateRecipientDisplay() - binding.searchConversation.setOnClickListener(this) - binding.clearMessages.setOnClickListener(this) - binding.allMedia.setOnClickListener(this) - binding.pinConversation.setOnClickListener(this) - binding.notificationSettings.setOnClickListener(this) - binding.editGroup.setOnClickListener(this) - binding.leaveGroup.setOnClickListener(this) - binding.back.setOnClickListener(this) - binding.autoDownloadMediaSwitch.setOnCheckedChangeListener { _, isChecked -> - viewModel.setAutoDownloadAttachments(isChecked) - updateRecipientDisplay() - } - } - - private fun updateRecipientDisplay() { - val recipient = viewModel.recipient ?: return - // Setup profile image - binding.profilePictureView.root.update(recipient) - // Setup name - binding.conversationName.text = when { - recipient.isLocalNumber -> getString(R.string.noteToSelf) - else -> recipient.toShortString() - } - // Setup group description (if group) - binding.conversationSubtitle.isVisible = recipient.isClosedGroupV2Recipient.apply { - binding.conversationSubtitle.text = viewModel.closedGroupInfo()?.description - } - - // Toggle group-specific settings - val areGroupOptionsVisible = recipient.isClosedGroupV2Recipient || recipient.isLegacyClosedGroupRecipient - groupOptions.forEach { v -> - v.isVisible = areGroupOptionsVisible - } - - // Group admin settings - val isUserGroupAdmin = areGroupOptionsVisible && viewModel.isUserGroupAdmin() - with (binding) { - groupMembersDivider.root.isVisible = areGroupOptionsVisible && !isUserGroupAdmin - groupMembers.isVisible = !isUserGroupAdmin - adminControlsGroup.isVisible = isUserGroupAdmin - deleteGroup.isVisible = isUserGroupAdmin - clearMessages.isVisible = isUserGroupAdmin - clearMessagesDivider.root.isVisible = isUserGroupAdmin - leaveGroupDivider.root.isVisible = isUserGroupAdmin - } - - // Set pinned state - binding.pinConversation.setText( - if (viewModel.isPinned()) R.string.pinUnpinConversation - else R.string.pinConversation - ) - - // Set auto-download state - val trusted = viewModel.autoDownloadAttachments() - binding.autoDownloadMediaSwitch.isChecked = trusted - - // Set notification type - val notifyTypes = resources.getStringArray(R.array.notify_types) - val summary = notifyTypes.getOrNull(recipient.notifyType) - binding.notificationsValue.text = summary - } - - override fun onClick(v: View?) { - val threadRecipient = viewModel.recipient ?: return - when { - v === binding.searchConversation -> { - setResult(RESULT_SEARCH) - finish() - } - v === binding.allMedia -> { - startActivity(MediaOverviewActivity.createIntent(this, threadRecipient.address)) - } - v === binding.pinConversation -> { - viewModel.togglePin().invokeOnCompletion { e -> - if (e != null) { - // something happened - Log.e("ConversationSettings", "Failed to toggle pin on thread", e) - } else { - updateRecipientDisplay() - } - } - } - v === binding.notificationSettings -> { - notificationActivityCallback.launch(viewModel.threadId) - } - v === binding.back -> onBackPressed() - v === binding.clearMessages -> { - - showSessionDialog { - title(R.string.clearMessages) - text(Phrase.from(this@ConversationSettingsActivity, R.string.clearMessagesChatDescription) - .put(NAME_KEY, threadRecipient.name) - .format()) - dangerButton( - R.string.clear, - R.string.clear) { - viewModel.clearMessages(false) - } - cancelButton() - } - } - v === binding.leaveGroup -> { - - if (threadRecipient.isLegacyClosedGroupRecipient) { - // Send a leave group message if this is an active closed group - val groupString = threadRecipient.address.toGroupString() - val ourId = TextSecurePreferences.getLocalNumber(this)!! - if (groupDb.isActive(groupString)) { - showSessionDialog { - - title(R.string.groupLeave) - - val name = viewModel.recipient!!.name!! - val textWithArgs = if (groupDb.getGroup(groupString).get().admins.map(Address::serialize).contains(ourId)) { - Phrase.from(context, R.string.groupLeaveDescriptionAdmin) - .put(GROUP_NAME_KEY, name) - .format() - } else { - Phrase.from(context, R.string.groupLeaveDescription) - .put(GROUP_NAME_KEY, name) - .format() - } - text(textWithArgs) - dangerButton( - R.string.groupLeave, - R.string.groupLeave - ) { - lifecycleScope.launch { - GroupUtil.doubleDecodeGroupID(threadRecipient.address.toString()) - .toHexString() - .let { MessageSender.explicitLeave(it, true, deleteThread = true) } - finish() - } - } - cancelButton() - } - try { - - } catch (e: IOException) { - Log.e("Loki", e) - } - } - } else if (threadRecipient.isClosedGroupV2Recipient) { - val groupInfo = viewModel.closedGroupInfo() - showSessionDialog { - - title(R.string.groupLeave) - - val name = viewModel.recipient!!.name!! - val textWithArgs = if (groupInfo?.isUserAdmin == true) { - Phrase.from(context, R.string.groupLeaveDescription) - .put(GROUP_NAME_KEY, name) - .format() - } else { - Phrase.from(context, R.string.groupLeaveDescription) - .put(GROUP_NAME_KEY, name) - .format() - } - text(textWithArgs) - dangerButton( - R.string.groupLeave, - R.string.groupLeave - ) { - lifecycleScope.launch { - viewModel.leaveGroup() - finish() - } - } - cancelButton() - } - } - } - v === binding.editGroup -> { - val recipient = viewModel.recipient ?: return - - val intent = when { - recipient.isLegacyClosedGroupRecipient -> Intent(this, EditLegacyGroupActivity::class.java).apply { - val groupID: String = recipient.address.toGroupString() - putExtra(EditLegacyGroupActivity.groupIDKey, groupID) - } - - recipient.isClosedGroupV2Recipient -> EditGroupActivity.createIntent( - context = this, - groupSessionId = recipient.address.serialize() - ) - - else -> return - } - startActivity(intent) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ConversationSettingsActivityContract.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ConversationSettingsActivityContract.kt deleted file mode 100644 index a79d94b3ae..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ConversationSettingsActivityContract.kt +++ /dev/null @@ -1,24 +0,0 @@ -package org.thoughtcrime.securesms.conversation.settings - -import android.content.Context -import android.content.Intent -import androidx.activity.result.contract.ActivityResultContract -import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 - -sealed class ConversationSettingsActivityResult { - object Finished: ConversationSettingsActivityResult() - object SearchConversation: ConversationSettingsActivityResult() -} - -class ConversationSettingsActivityContract: ActivityResultContract() { - - override fun createIntent(context: Context, input: Long) = Intent(context, ConversationSettingsActivity::class.java).apply { - putExtra(ConversationActivityV2.THREAD_ID, input ?: -1L) - } - - override fun parseResult(resultCode: Int, intent: Intent?): ConversationSettingsActivityResult = - when (resultCode) { - ConversationSettingsActivity.RESULT_SEARCH -> ConversationSettingsActivityResult.SearchConversation - else -> ConversationSettingsActivityResult.Finished - } -} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ConversationSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ConversationSettingsViewModel.kt deleted file mode 100644 index 17411b2211..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ConversationSettingsViewModel.kt +++ /dev/null @@ -1,104 +0,0 @@ -package org.thoughtcrime.securesms.conversation.settings - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.viewModelScope -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import network.loki.messenger.libsession_util.util.GroupDisplayInfo -import org.session.libsession.database.StorageProtocol -import org.session.libsession.messaging.jobs.JobQueue -import org.session.libsession.messaging.jobs.LibSessionGroupLeavingJob -import org.session.libsession.utilities.Address -import org.session.libsession.utilities.TextSecurePreferences -import org.session.libsignal.utilities.AccountId - -class ConversationSettingsViewModel( - val threadId: Long, - private val storage: StorageProtocol, - private val prefs: TextSecurePreferences -): ViewModel() { - - val recipient get() = storage.getRecipientForThread(threadId) - - fun isPinned() = storage.isPinned(threadId) - - fun togglePin() = viewModelScope.launch { - val isPinned = storage.isPinned(threadId) - storage.setPinned(threadId, !isPinned) - } - - fun autoDownloadAttachments() = recipient?.let { recipient -> storage.shouldAutoDownloadAttachments(recipient) } ?: false - - fun setAutoDownloadAttachments(shouldDownload: Boolean) { - recipient?.let { recipient -> storage.setAutoDownloadAttachments(recipient, shouldDownload) } - } - - fun isUserGroupAdmin(): Boolean = recipient?.let { recipient -> - when { - recipient.isLegacyClosedGroupRecipient -> { - 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 - } - recipient.isClosedGroupV2Recipient -> { - val group = storage.getLibSessionClosedGroup(recipient.address.serialize()) ?: return@let false - group.adminKey != null - } - else -> false - } - } ?: false - - fun clearMessages(forAll: Boolean) { - if (forAll && !isUserGroupAdmin()) return - - if (!forAll) { - viewModelScope.launch { - storage.clearMessages(threadId) - } - } else { - // do a send message here and on success do a clear messages - viewModelScope.launch { - storage.clearMessages(threadId) - } - } - } - - fun closedGroupInfo(): GroupDisplayInfo? = recipient - ?.address - ?.takeIf { it.isClosedGroupV2 } - ?.serialize() - ?.let(storage::getClosedGroupDisplayInfo) - - // Assume that user has verified they don't want to add a new admin etc - suspend fun leaveGroup() { - val recipient = recipient ?: return - return withContext(Dispatchers.IO) { - val groupLeave = LibSessionGroupLeavingJob( - AccountId(recipient.address.serialize()), - true - ) - JobQueue.shared.add(groupLeave) - } - } - - // DI-related - @dagger.assisted.AssistedFactory - interface AssistedFactory { - fun create(threadId: Long): Factory - } - class Factory @AssistedInject constructor( - @Assisted private val threadId: Long, - private val storage: StorageProtocol, - private val prefs: TextSecurePreferences - ) : ViewModelProvider.Factory { - @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T { - return ConversationSettingsViewModel(threadId, storage, prefs) as T - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 345253bda1..7f526c518d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -119,8 +119,6 @@ import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel import org.thoughtcrime.securesms.contacts.SelectContactsActivity.Companion.selectedContactsKey import org.thoughtcrime.securesms.conversation.ConversationActionBarDelegate import org.thoughtcrime.securesms.conversation.disappearingmessages.DisappearingMessagesActivity -import org.thoughtcrime.securesms.conversation.settings.ConversationSettingsActivityContract -import org.thoughtcrime.securesms.conversation.settings.ConversationSettingsActivityResult import org.thoughtcrime.securesms.conversation.v2.ConversationReactionOverlay.OnActionSelectedListener import org.thoughtcrime.securesms.conversation.v2.ConversationReactionOverlay.OnReactionSelectedListener import org.thoughtcrime.securesms.conversation.v2.MessageDetailActivity.Companion.MESSAGE_TIMESTAMP @@ -164,6 +162,7 @@ import org.thoughtcrime.securesms.database.model.MessageId import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.database.model.ReactionRecord +import org.thoughtcrime.securesms.dependencies.ConfigFactory import org.thoughtcrime.securesms.giph.ui.GiphyActivity import org.thoughtcrime.securesms.groups.OpenGroupManager import org.thoughtcrime.securesms.home.search.getSearchName @@ -222,7 +221,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe ConversationActionModeCallbackDelegate, VisibleMessageViewDelegate, RecipientModifiedListener, SearchBottomBar.EventListener, LoaderManager.LoaderCallbacks, ConversationActionBarDelegate, OnReactionSelectedListener, ReactWithAnyEmojiDialogFragment.Callback, ReactionsDialogFragment.Callback, - ConversationMenuHelper.ConversationMenuListener, View.OnClickListener { + ConversationMenuHelper.ConversationMenuListener { private lateinit var binding: ActivityConversationV2Binding @@ -239,6 +238,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe @Inject lateinit var reactionDb: ReactionDatabase @Inject lateinit var viewModelFactory: ConversationViewModel.AssistedFactory @Inject lateinit var mentionViewModelFactory: MentionViewModel.AssistedFactory + @Inject lateinit var configFactory: ConfigFactory private val screenshotObserver by lazy { ScreenshotObserver(this, Handler(Looper.getMainLooper())) { @@ -247,13 +247,6 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe } } - private val conversationSettingsCallback = registerForActivityResult(ConversationSettingsActivityContract()) { result -> - if (result is ConversationSettingsActivityResult.SearchConversation) { - // open search - binding?.toolbar?.menu?.findItem(R.id.menu_search)?.expandActionView() - } - } - private val screenWidth = Resources.getSystem().displayMetrics.widthPixels private val linkPreviewViewModel: LinkPreviewViewModel by lazy { ViewModelProvider(this, LinkPreviewViewModel.Factory(LinkPreviewRepository())) @@ -497,7 +490,6 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe updatePlaceholder() setUpBlockedBanner() binding.searchBottomBar.setEventListener(this) - binding.toolbarContent.profilePictureView.setOnClickListener(this) updateSendAfterApprovalText() setUpMessageRequests() @@ -949,10 +941,11 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe val recipient = viewModel.recipient ?: return false if (!viewModel.isMessageRequestThread) { ConversationMenuHelper.onPrepareOptionsMenu( - menu, - menuInflater, - recipient, - this + menu = menu, + inflater = menuInflater, + thread = recipient, + context = this, + configFactory = configFactory, ) } maybeUpdateToolbar(recipient) @@ -1219,17 +1212,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe return false } return viewModel.recipient?.let { recipient -> - ConversationMenuHelper.onOptionItemSelected(this, item, recipient) + ConversationMenuHelper.onOptionItemSelected(this, item, recipient, configFactory, storage) } ?: false } - override fun onClick(v: View?) { - if (v === binding?.toolbarContent?.profilePictureView) { - // open conversation settings - conversationSettingsCallback.launch(viewModel.threadId) - } - } - override fun block(deleteThread: Boolean) { val recipient = viewModel.recipient ?: return Log.w("Loki", "Recipient was null for block action") val invitingAdmin = viewModel.invitingAdmin diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt index d16c46946b..4a0cf31d00 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationMenuHelper.kt @@ -5,7 +5,6 @@ import android.annotation.SuppressLint import android.content.Context import android.content.Intent import android.graphics.BitmapFactory -import android.net.Uri import android.os.AsyncTask import android.view.Menu import android.view.MenuInflater @@ -21,13 +20,14 @@ import androidx.core.graphics.drawable.IconCompat import com.squareup.phrase.Phrase import java.io.IOException import network.loki.messenger.R +import org.session.libsession.database.StorageProtocol import org.session.libsession.messaging.sending_receiving.MessageSender import org.session.libsession.messaging.sending_receiving.leave import org.session.libsession.utilities.GroupUtil.doubleDecodeGroupID -import org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY import org.session.libsession.utilities.StringSubstitutionConstants.GROUP_NAME_KEY import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.recipients.Recipient +import org.session.libsignal.utilities.AccountId import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.guava.Optional import org.session.libsignal.utilities.toHexString @@ -38,6 +38,8 @@ import org.thoughtcrime.securesms.calls.WebRtcCallActivity import org.thoughtcrime.securesms.contacts.SelectContactsActivity import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 import org.thoughtcrime.securesms.conversation.v2.utilities.NotificationUtils +import org.thoughtcrime.securesms.database.Storage +import org.thoughtcrime.securesms.dependencies.ConfigFactory import org.thoughtcrime.securesms.dependencies.DatabaseComponent import org.thoughtcrime.securesms.groups.EditLegacyGroupActivity import org.thoughtcrime.securesms.groups.EditLegacyGroupActivity.Companion.groupIDKey @@ -54,7 +56,8 @@ object ConversationMenuHelper { menu: Menu, inflater: MenuInflater, thread: Recipient, - context: Context + context: Context, + configFactory: ConfigFactory, ) { // Prepare menu.clear() @@ -77,10 +80,20 @@ object ConversationMenuHelper { inflater.inflate(R.menu.menu_conversation_block, menu) } } - // Closed group menu (options that should only be present in closed groups) + // (Legacy) Closed group menu (options that should only be present in closed groups) if (thread.isLegacyClosedGroupRecipient) { - inflater.inflate(R.menu.menu_conversation_closed_group, menu) + inflater.inflate(R.menu.menu_conversation_legacy_group, menu) } + + // Groups v2 menu + if (thread.isClosedGroupV2Recipient) { + if (configFactory.userGroups?.getClosedGroup(thread.address.serialize())?.hasAdminKey() == true) { + inflater.inflate(R.menu.menu_conversation_groups_v2_admin, menu) + } + + inflater.inflate(R.menu.menu_conversation_groups_v2, menu) + } + // Open group menu if (isCommunity) { inflater.inflate(R.menu.menu_conversation_open_group, menu) @@ -134,7 +147,13 @@ object ConversationMenuHelper { }) } - fun onOptionItemSelected(context: Context, item: MenuItem, thread: Recipient): Boolean { + fun onOptionItemSelected( + context: Context, + item: MenuItem, + thread: Recipient, + factory: ConfigFactory, + storage: StorageProtocol + ): Boolean { when (item.itemId) { R.id.menu_view_all_media -> { showAllMedia(context, thread) } R.id.menu_search -> { search(context) } @@ -146,7 +165,7 @@ object ConversationMenuHelper { R.id.menu_copy_account_id -> { copyAccountID(context, thread) } R.id.menu_copy_open_group_url -> { copyOpenGroupUrl(context, thread) } R.id.menu_edit_group -> { editClosedGroup(context, thread) } - R.id.menu_leave_group -> { leaveClosedGroup(context, thread) } + R.id.menu_leave_group -> { leaveClosedGroup(context, thread, factory, storage) } R.id.menu_invite_to_open_group -> { inviteContacts(context, thread) } R.id.menu_unmute_notifications -> { unmute(context, thread) } R.id.menu_mute_notifications -> { mute(context, thread) } @@ -278,26 +297,67 @@ object ConversationMenuHelper { context.startActivity(intent) } - private fun leaveClosedGroup(context: Context, thread: Recipient) { - if (!thread.isLegacyClosedGroupRecipient) { return } + private fun leaveClosedGroup( + context: Context, + thread: Recipient, + configFactory: ConfigFactory, + storage: StorageProtocol + ) { + when { + thread.isLegacyClosedGroupRecipient -> { + val group = DatabaseComponent.get(context).groupDatabase().getGroup(thread.address.toGroupString()).orNull() + val admins = group.admins + val accountID = TextSecurePreferences.getLocalNumber(context) + val isCurrentUserAdmin = admins.any { it.toString() == accountID } - val group = DatabaseComponent.get(context).groupDatabase().getGroup(thread.address.toGroupString()).orNull() - val admins = group.admins - val accountID = TextSecurePreferences.getLocalNumber(context) - val isCurrentUserAdmin = admins.any { it.toString() == accountID } - val message = if (isCurrentUserAdmin) { + confirmAndLeaveClosedGroup(context, group.title, isCurrentUserAdmin, doLeave = { + val groupPublicKey = doubleDecodeGroupID(thread.address.toString()).toHexString() + + check(DatabaseComponent.get(context).lokiAPIDatabase().isClosedGroup(groupPublicKey)) { + "Invalid group public key" + } + MessageSender.leave(groupPublicKey, notifyUser = false) + }) + } + + thread.isClosedGroupV2Recipient -> { + val accountId = AccountId(thread.address.serialize()) + val group = configFactory.userGroups?.getClosedGroup(accountId.hexString) ?: return + val (name, isAdmin) = configFactory.getGroupInfoConfig(accountId)?.use { + it.getName() to group.hasAdminKey() + } ?: return + + confirmAndLeaveClosedGroup( + context = context, + groupName = name, + isAdmin = isAdmin, + doLeave = { + check(storage.leaveGroup(accountId.hexString, true)) + } + ) + } + } + } + + private fun confirmAndLeaveClosedGroup( + context: Context, + groupName: String, + isAdmin: Boolean, + doLeave: () -> Unit, + ) { + val message = if (isAdmin) { Phrase.from(context, R.string.groupDeleteDescription) - .put(GROUP_NAME_KEY, group.title) + .put(GROUP_NAME_KEY, groupName) .format() } else { Phrase.from(context, R.string.groupLeaveDescription) - .put(GROUP_NAME_KEY, group.title) + .put(GROUP_NAME_KEY, groupName) .format() } fun onLeaveFailed() { val txt = Phrase.from(context, R.string.groupLeaveErrorFailed) - .put(GROUP_NAME_KEY, group.title) + .put(GROUP_NAME_KEY, groupName) .format().toString() Toast.makeText(context, txt, Toast.LENGTH_LONG).show() } @@ -307,11 +367,7 @@ object ConversationMenuHelper { text(message) dangerButton(R.string.leave) { try { - val groupPublicKey = doubleDecodeGroupID(thread.address.toString()).toHexString() - val isClosedGroup = DatabaseComponent.get(context).lokiAPIDatabase().isClosedGroup(groupPublicKey) - - if (isClosedGroup) MessageSender.leave(groupPublicKey, notifyUser = false) - else onLeaveFailed() + doLeave() } catch (e: Exception) { onLeaveFailed() } diff --git a/app/src/main/res/layout/activity_conversation_notification_settings.xml b/app/src/main/res/layout/activity_conversation_notification_settings.xml deleted file mode 100644 index 74ba7632e1..0000000000 --- a/app/src/main/res/layout/activity_conversation_notification_settings.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_conversation_settings.xml b/app/src/main/res/layout/activity_conversation_settings.xml deleted file mode 100644 index d5f810d2cb..0000000000 --- a/app/src/main/res/layout/activity_conversation_settings.xml +++ /dev/null @@ -1,375 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/menu/menu_conversation_groups_v2.xml b/app/src/main/res/menu/menu_conversation_groups_v2.xml new file mode 100644 index 0000000000..e519278931 --- /dev/null +++ b/app/src/main/res/menu/menu_conversation_groups_v2.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_conversation_groups_v2_admin.xml b/app/src/main/res/menu/menu_conversation_groups_v2_admin.xml new file mode 100644 index 0000000000..2cab0f9c5b --- /dev/null +++ b/app/src/main/res/menu/menu_conversation_groups_v2_admin.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_conversation_closed_group.xml b/app/src/main/res/menu/menu_conversation_legacy_group.xml similarity index 100% rename from app/src/main/res/menu/menu_conversation_closed_group.xml rename to app/src/main/res/menu/menu_conversation_legacy_group.xml