From a3aeceb0414cd1ad5b7c0b21eb3bab6087b7860c Mon Sep 17 00:00:00 2001 From: jeffrey-signal Date: Thu, 20 Nov 2025 16:27:38 -0500 Subject: [PATCH] Enable split pane UI for add to groups screen. --- app/src/main/AndroidManifest.xml | 6 +- .../ConversationSettingsFragment.kt | 2 +- .../ui/addtogroup/AddToGroupViewModel.java | 139 --------------- .../ui/addtogroup/AddToGroupsActivity.java | 167 ------------------ ...psActivityV2.kt => AddToGroupsActivity.kt} | 8 +- ...ViewModelV2.kt => AddToGroupsViewModel.kt} | 7 +- .../bottomsheet/RecipientDialogViewModel.java | 2 +- .../main/res/layout/add_to_group_activity.xml | 58 ------ 8 files changed, 8 insertions(+), 381 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupViewModel.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupsActivity.java rename app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/{AddToGroupsActivityV2.kt => AddToGroupsActivity.kt} (97%) rename app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/{AddToGroupsViewModelV2.kt => AddToGroupsViewModel.kt} (96%) delete mode 100644 app/src/main/res/layout/add_to_group_activity.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 308803275e..1d061b6057 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1059,12 +1059,8 @@ android:exported="false" android:theme="@style/Signal.DayNight.NoActionBar" /> - - diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt index 24fe681f49..d277b3c2ac 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt @@ -944,7 +944,7 @@ class ConversationSettingsFragment : DSLSettingsFragment( } private fun handleAddToAGroup(addToAGroup: ConversationSettingsEvent.AddToAGroup) { - startActivity(AddToGroupsActivity.newIntent(requireContext(), addToAGroup.recipientId, addToAGroup.groupMembership)) + startActivity(AddToGroupsActivity.createIntent(requireContext(), addToAGroup.recipientId, addToAGroup.groupMembership)) } @Suppress("DEPRECATION") diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupViewModel.java deleted file mode 100644 index 66b439c611..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupViewModel.java +++ /dev/null @@ -1,139 +0,0 @@ -package org.thoughtcrime.securesms.groups.ui.addtogroup; - -import android.app.Application; - -import androidx.annotation.NonNull; -import androidx.lifecycle.ViewModel; -import androidx.lifecycle.ViewModelProvider; - -import org.signal.core.util.concurrent.SignalExecutors; -import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.dependencies.AppDependencies; -import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason; -import org.thoughtcrime.securesms.groups.ui.GroupErrors; -import org.thoughtcrime.securesms.groups.v2.GroupAddMembersResult; -import org.thoughtcrime.securesms.groups.v2.GroupManagementRepository; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientId; -import org.thoughtcrime.securesms.util.SingleLiveEvent; - -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -public final class AddToGroupViewModel extends ViewModel { - - private final Application context; - private final GroupManagementRepository repository; - private final RecipientId recipientId; - private final SingleLiveEvent events = new SingleLiveEvent<>(); - - private AddToGroupViewModel(@NonNull RecipientId recipientId) { - this.context = AppDependencies.getApplication(); - this.recipientId = recipientId; - this.repository = new GroupManagementRepository(); - } - - public SingleLiveEvent getEvents() { - return events; - } - - void onContinueWithSelection(@NonNull List groupRecipientIds) { - if (groupRecipientIds.isEmpty()) { - events.postValue(new Event.CloseEvent()); - } else if (groupRecipientIds.size() == 1) { - SignalExecutors.BOUNDED.execute(() -> { - Recipient recipient = Recipient.resolved(recipientId); - Recipient groupRecipient = Recipient.resolved(groupRecipientIds.get(0)); - String recipientName = recipient.getDisplayName(context); - String groupName = groupRecipient.getDisplayName(context); - - if (groupRecipient.getGroupId().get().isV1() && !recipient.getHasE164()) { - events.postValue(new Event.LegacyGroupDenialEvent()); - } else { - events.postValue(new Event.AddToSingleGroupConfirmationEvent(context.getResources().getString(R.string.AddToGroupActivity_add_member), - context.getResources().getString(R.string.AddToGroupActivity_add_s_to_s, recipientName, groupName), - groupRecipient, recipientName, groupName)); - } - }); - } else { - throw new AssertionError("Does not support multi-select"); - } - } - - void onAddToGroupsConfirmed(@NonNull Event.AddToSingleGroupConfirmationEvent event) { - repository.addMembers(event.groupRecipient, Collections.singletonList(recipientId), result -> { - if (result.isFailure()) { - GroupChangeFailureReason reason = ((GroupAddMembersResult.Failure) result).getReason(); - events.postValue(new Event.ToastEvent(context.getResources().getString(GroupErrors.getUserDisplayMessage(reason)))); - } else { - events.postValue(new Event.ToastEvent(context.getResources().getString(R.string.AddToGroupActivity_s_added_to_s, event.recipientName, event.groupName))); - events.postValue(new Event.CloseEvent()); - } - }); - } - - static abstract class Event { - - static class CloseEvent extends Event { - } - - static class ToastEvent extends Event { - private final String message; - - ToastEvent(@NonNull String message) { - this.message = message; - } - - public String getMessage() { - return message; - } - } - - static class AddToSingleGroupConfirmationEvent extends Event { - private final String title; - private final String message; - private final Recipient groupRecipient; - private final String recipientName; - private final String groupName; - - AddToSingleGroupConfirmationEvent(@NonNull String title, - @NonNull String message, - @NonNull Recipient groupRecipient, - @NonNull String recipientName, - @NonNull String groupName) - { - this.title = title; - this.message = message; - this.groupRecipient = groupRecipient; - this.recipientName = recipientName; - this.groupName = groupName; - } - - String getTitle() { - return title; - } - - String getMessage() { - return message; - } - } - - static class LegacyGroupDenialEvent extends Event { - } - } - - public static class Factory implements ViewModelProvider.Factory { - - private final RecipientId recipientId; - - public Factory(@NonNull RecipientId recipientId) { - this.recipientId = recipientId; - } - - @Override - public @NonNull T create(@NonNull Class modelClass) { - return Objects.requireNonNull(modelClass.cast(new AddToGroupViewModel(recipientId))); - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupsActivity.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupsActivity.java deleted file mode 100644 index a1bdf3cf25..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupsActivity.java +++ /dev/null @@ -1,167 +0,0 @@ -package org.thoughtcrime.securesms.groups.ui.addtogroup; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.view.MenuItem; -import android.view.View; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.lifecycle.ViewModelProvider; - -import com.annimon.stream.Stream; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; - -import org.thoughtcrime.securesms.ContactSelectionActivity; -import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.contacts.ContactSelectionDisplayMode; -import org.thoughtcrime.securesms.contacts.paged.ChatType; -import org.thoughtcrime.securesms.contacts.selection.ContactSelectionArguments; -import org.thoughtcrime.securesms.groups.ui.addtogroup.AddToGroupViewModel.Event; -import org.thoughtcrime.securesms.recipients.RecipientId; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.function.Consumer; - -/** - * Group selection activity, will add a single member to selected groups. - */ -public final class AddToGroupsActivity extends ContactSelectionActivity { - - private static final int MINIMUM_GROUP_SELECT_SIZE = 1; - - private static final String EXTRA_RECIPIENT_ID = "RECIPIENT_ID"; - - private View next; - private AddToGroupViewModel viewModel; - - public static Intent newIntent(@NonNull Context context, - @NonNull RecipientId recipientId, - @NonNull List currentGroupsMemberOf) - { - Intent intent = new Intent(context, AddToGroupsActivity.class); - - intent.putExtra(ContactSelectionArguments.REFRESHABLE, false); - intent.putExtra(ContactSelectionArguments.RECENTS, true); - intent.putExtra(ContactSelectionActivity.EXTRA_LAYOUT_RES_ID, R.layout.add_to_group_activity); - intent.putExtra(EXTRA_RECIPIENT_ID, recipientId); - - intent.putExtra(ContactSelectionArguments.DISPLAY_MODE, ContactSelectionDisplayMode.FLAG_ACTIVE_GROUPS | ContactSelectionDisplayMode.FLAG_GROUPS_AFTER_CONTACTS); - - intent.putParcelableArrayListExtra(ContactSelectionArguments.CURRENT_SELECTION, new ArrayList<>(currentGroupsMemberOf)); - - return intent; - } - - @Override - public void onCreate(Bundle bundle, boolean ready) { - super.onCreate(bundle, ready); - - Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true); - - next = findViewById(R.id.next); - - getContactFilterView().setHint(contactsFragment.isMulti() ? R.string.AddToGroupActivity_add_to_groups : R.string.AddToGroupActivity_add_to_group); - - next.setVisibility(contactsFragment.isMulti() ? View.VISIBLE : View.GONE); - - disableNext(); - next.setOnClickListener(v -> handleNextPressed()); - - AddToGroupViewModel.Factory factory = new AddToGroupViewModel.Factory(getRecipientId()); - viewModel = new ViewModelProvider(this, factory).get(AddToGroupViewModel.class); - - - viewModel.getEvents().observe(this, event -> { - if (event instanceof Event.CloseEvent) { - finish(); - } else if (event instanceof Event.ToastEvent) { - Toast.makeText(this, ((Event.ToastEvent) event).getMessage(), Toast.LENGTH_SHORT).show(); - } else if (event instanceof Event.AddToSingleGroupConfirmationEvent) { - Event.AddToSingleGroupConfirmationEvent addEvent = (Event.AddToSingleGroupConfirmationEvent) event; - new MaterialAlertDialogBuilder(this) - .setTitle(addEvent.getTitle()) - .setMessage(addEvent.getMessage()) - .setPositiveButton(R.string.AddToGroupActivity_add, (dialog, which) -> viewModel.onAddToGroupsConfirmed(addEvent)) - .setNegativeButton(android.R.string.cancel, null) - .show(); - } else if (event instanceof Event.LegacyGroupDenialEvent) { - Toast.makeText(this, R.string.AddToGroupActivity_this_person_cant_be_added_to_legacy_groups, Toast.LENGTH_SHORT).show(); - } else { - throw new AssertionError(); - } - }); - } - - private @NonNull RecipientId getRecipientId() { - return getIntent().getParcelableExtra(EXTRA_RECIPIENT_ID); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == android.R.id.home) { - finish(); - return true; - } - - return super.onOptionsItemSelected(item); - } - - @Override - public void onBeforeContactSelected(boolean isFromUnknownSearchKey, @NonNull Optional recipientId, String number, @NonNull Optional chatType, @NonNull Consumer callback) { - if (contactsFragment.isMulti()) { - throw new UnsupportedOperationException("Not yet built to handle multi-select."); -// if (contactsFragment.hasQueryFilter()) { -// getToolbar().clear(); -// } -// -// if (contactsFragment.getSelectedContactsCount() >= MINIMUM_GROUP_SELECT_SIZE) { -// enableNext(); -// } - } else { - if (recipientId.isPresent()) { - viewModel.onContinueWithSelection(Collections.singletonList(recipientId.get())); - } - } - - callback.accept(true); - } - - @Override - public void onContactDeselected(@NonNull Optional recipientId, String number, @NonNull Optional chatType) { - if (contactsFragment.hasQueryFilter()) { - getContactFilterView().clear(); - } - - if (contactsFragment.getSelectedContactsCount() < MINIMUM_GROUP_SELECT_SIZE) { - disableNext(); - } - } - - @Override - public void onSelectionChanged() { - } - - private void enableNext() { - next.setEnabled(true); - next.animate().alpha(1f); - } - - private void disableNext() { - next.setEnabled(false); - next.animate().alpha(0.5f); - } - - private void handleNextPressed() { - List groupsRecipientIds = Stream.of(contactsFragment.getSelectedContacts()) - .map(selectedContact -> selectedContact.getOrCreateRecipientId()) - .toList(); - - viewModel.onContinueWithSelection(groupsRecipientIds); - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupsActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupsActivity.kt similarity index 97% rename from app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupsActivityV2.kt rename to app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupsActivity.kt index 8e641caa68..7e5a9ab852 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupsActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupsActivity.kt @@ -48,7 +48,7 @@ import org.thoughtcrime.securesms.recipients.ui.RecipientSelection /** * Allows the user to add a recipient to a group. */ -class AddToGroupsActivityV2 : PassphraseRequiredActivity() { +class AddToGroupsActivity : PassphraseRequiredActivity() { companion object { private const val EXTRA_RECIPIENT_ID = "recipient_id" private const val EXTRA_SELECTION_LIMITS = "selection_limits" @@ -62,7 +62,7 @@ class AddToGroupsActivityV2 : PassphraseRequiredActivity() { existingGroupMemberships: List, selectionLimits: SelectionLimits? = null ): Intent { - return Intent(context, AddToGroupsActivityV2::class.java).apply { + return Intent(context, AddToGroupsActivity::class.java).apply { putExtra(EXTRA_RECIPIENT_ID, recipientId) putExtra(EXTRA_SELECTION_LIMITS, selectionLimits) putParcelableArrayListExtra(EXTRA_PRESELECTED_GROUPS, ArrayList(existingGroupMemberships)) @@ -80,7 +80,7 @@ class AddToGroupsActivityV2 : PassphraseRequiredActivity() { SignalTheme { AddToGroupsScreen( viewModel = viewModel { - AddToGroupsViewModelV2( + AddToGroupsViewModel( recipientId = intent.getParcelableExtraCompat(EXTRA_RECIPIENT_ID, RecipientId::class.java)!!, selectionLimits = intent.getParcelableExtraCompat(EXTRA_SELECTION_LIMITS, SelectionLimits::class.java), existingGroupMemberships = intent.getParcelableArrayListExtraCompat(EXTRA_PRESELECTED_GROUPS, RecipientId::class.java)!!.toSet() @@ -95,7 +95,7 @@ class AddToGroupsActivityV2 : PassphraseRequiredActivity() { @Composable private fun AddToGroupsScreen( - viewModel: AddToGroupsViewModelV2, + viewModel: AddToGroupsViewModel, closeScreen: () -> Unit ) { val callbacks = remember { diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupsViewModelV2.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupsViewModel.kt similarity index 96% rename from app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupsViewModelV2.kt rename to app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupsViewModel.kt index 9614b38fbd..07dc92a1a9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupsViewModelV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addtogroup/AddToGroupsViewModel.kt @@ -14,7 +14,6 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.contacts.SelectedContact import org.thoughtcrime.securesms.groups.SelectionLimits import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason @@ -25,16 +24,12 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId -class AddToGroupsViewModelV2( +class AddToGroupsViewModel( private val recipientId: RecipientId, private val existingGroupMemberships: Set, selectionLimits: SelectionLimits? ) : ViewModel() { - companion object { - private val TAG = Log.tag(AddToGroupsViewModelV2::class) - } - private val internalUiState = MutableStateFlow( AddToGroupsUiState( existingGroupMemberships = existingGroupMemberships, diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogViewModel.java index f697be8f3f..4c7c123400 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogViewModel.java @@ -262,7 +262,7 @@ final class RecipientDialogViewModel extends ViewModel { } void onAddToGroupButton(@NonNull Activity activity) { - recipientDialogRepository.getGroupMembership(existingGroups -> activity.startActivity(AddToGroupsActivity.newIntent(activity, recipientDialogRepository.getRecipientId(), existingGroups))); + recipientDialogRepository.getGroupMembership(existingGroups -> activity.startActivity(AddToGroupsActivity.createIntent(activity, recipientDialogRepository.getRecipientId(), existingGroups))); } @WorkerThread diff --git a/app/src/main/res/layout/add_to_group_activity.xml b/app/src/main/res/layout/add_to_group_activity.xml deleted file mode 100644 index 37a284a134..0000000000 --- a/app/src/main/res/layout/add_to_group_activity.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file