From 94241f706867cfa99c96a6ea47b44765633ba0df Mon Sep 17 00:00:00 2001 From: jeffrey-signal Date: Tue, 18 Nov 2025 13:45:31 -0500 Subject: [PATCH] Enable split pane UI for add group members screen. --- app/src/main/AndroidManifest.xml | 7 +- .../ConversationSettingsFragment.kt | 7 +- .../ui/addmembers/AddMembersActivity.java | 199 ------------------ ...ersActivityV2.kt => AddMembersActivity.kt} | 12 +- .../ui/addmembers/AddMembersRepository.java | 33 --- .../ui/addmembers/AddMembersViewModel.java | 114 ---------- ...sViewModelV2.kt => AddMembersViewModel.kt} | 2 +- .../main/res/layout/add_members_activity.xml | 56 ----- app/src/main/res/values/strings.xml | 15 +- 9 files changed, 14 insertions(+), 431 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersActivity.java rename app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/{AddMembersActivityV2.kt => AddMembersActivity.kt} (97%) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersRepository.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersViewModel.java rename app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/{AddMembersViewModelV2.kt => AddMembersViewModel.kt} (99%) delete mode 100644 app/src/main/res/layout/add_members_activity.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 47712408f9..52c8bd9d30 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1063,9 +1063,10 @@ android:theme="@style/Theme.Signal.DayNight.NoActionBar" android:exported="false"/> - + findByActivityLauncher; - - public static @NonNull Intent createIntent(@NonNull Context context, - @NonNull GroupId groupId, - int displayModeFlags, - int selectionWarning, - int selectionLimit, - @NonNull List membersWithoutSelf) { - Intent intent = new Intent(context, AddMembersActivity.class); - intent.putExtra(GROUP_ID, groupId.toString()); - intent.putExtra(ContactSelectionArguments.DISPLAY_MODE, displayModeFlags); - intent.putExtra(ContactSelectionArguments.SELECTION_LIMITS, new SelectionLimits(selectionWarning, selectionLimit)); - intent.putParcelableArrayListExtra(ContactSelectionArguments.CURRENT_SELECTION, new ArrayList<>(membersWithoutSelf)); - intent.putExtra(ContactSelectionArguments.RV_PADDING_BOTTOM, (int) DimensionUnit.DP.toPixels(64f)); - intent.putExtra(ContactSelectionArguments.RV_CLIP, false); - - return intent; - } - - @Override - protected void onCreate(Bundle icicle, boolean ready) { - getIntent().putExtra(ContactSelectionActivity.EXTRA_LAYOUT_RES_ID, R.layout.add_members_activity); - super.onCreate(icicle, ready); - - AddMembersViewModel.Factory factory = new AddMembersViewModel.Factory(getGroupId()); - - done = findViewById(R.id.done); - viewModel = new ViewModelProvider(this, factory).get(AddMembersViewModel.class); - - done.setOnClickListener(v -> - viewModel.getDialogStateForSelectedContacts(contactsFragment.getSelectedContacts(), this::displayAlertMessage) - ); - - disableDone(); - - findByActivityLauncher = registerForActivityResult(new FindByActivity.Contract(), result -> { - if (result != null) { - contactsFragment.addRecipientToSelectionIfAble(result); - } - }); - } - - @Override - protected void initializeToolbar() { - getToolbar().setNavigationIcon(R.drawable.symbol_arrow_start_24); - getToolbar().setNavigationOnClickListener(v -> { - setResult(RESULT_CANCELED); - finish(); - }); - } - - @Override - public void onBeforeContactSelected(boolean isFromUnknownSearchKey, @NonNull Optional recipientId, String number, @NonNull Optional chatType, @NonNull Consumer callback) { - if (getGroupId().isV1() && recipientId.isPresent() && !Recipient.resolved(recipientId.get()).getHasE164()) { - Toast.makeText(this, R.string.AddMembersActivity__this_person_cant_be_added_to_legacy_groups, Toast.LENGTH_SHORT).show(); - callback.accept(false); - return; - } - - if (contactsFragment.hasQueryFilter()) { - getContactFilterView().clear(); - } - - if (recipientId.isPresent()) { - callback.accept(true); - enableDone(); - return; - } - - AlertDialog progress = SimpleProgressDialog.show(this); - - SimpleTask.run(getLifecycle(), () -> RecipientRepository.lookupNewE164(number), result -> { - progress.dismiss(); - - if (result instanceof RecipientRepository.LookupResult.Success) { - enableDone(); - callback.accept(true); - } else if (result instanceof RecipientRepository.PhoneLookupResult.NotFound || result instanceof RecipientRepository.PhoneLookupResult.InvalidPhone) { - new MaterialAlertDialogBuilder(this) - .setMessage(getString(R.string.RecipientLookup_error__s_is_not_a_signal_user, number)) - .setPositiveButton(android.R.string.ok, null) - .show(); - callback.accept(false); - } else { - new MaterialAlertDialogBuilder(this) - .setMessage(R.string.NetworkFailure__network_error_check_your_connection_and_try_again) - .setPositiveButton(android.R.string.ok, null) - .show(); - callback.accept(false); - } - }); - } - - @Override - public void onContactDeselected(@NonNull Optional recipientId, String number, @NonNull Optional chatType) { - if (contactsFragment.hasQueryFilter()) { - getContactFilterView().clear(); - } - - if (contactsFragment.getSelectedContactsCount() < 1) { - disableDone(); - } - } - - @Override - public void onSelectionChanged() { - int selectedContactsCount = contactsFragment.getTotalMemberCount() + 1; - if (selectedContactsCount == 0) { - getToolbar().setTitle(getString(R.string.AddMembersActivity__add_members)); - } else { - getToolbar().setTitle(getResources().getQuantityString(R.plurals.CreateGroupActivity__d_members, selectedContactsCount, selectedContactsCount)); - } - } - - @Override - public void onFindByPhoneNumber() { - findByActivityLauncher.launch(FindByMode.PHONE_NUMBER); - } - - @Override - public void onFindByUsername() { - findByActivityLauncher.launch(FindByMode.USERNAME); - } - - private void enableDone() { - done.setEnabled(true); - done.animate().alpha(1f); - } - - private void disableDone() { - done.setEnabled(false); - done.animate().alpha(0.5f); - } - - private GroupId getGroupId() { - return GroupId.parseOrThrow(getIntent().getStringExtra(GROUP_ID)); - } - - private void displayAlertMessage(@NonNull AddMembersViewModel.AddMemberDialogMessageState state) { - Recipient recipient = Util.firstNonNull(state.getRecipient(), Recipient.UNKNOWN); - - String message = getResources().getQuantityString(R.plurals.AddMembersActivity__add_d_members_to_s, state.getSelectionCount(), - recipient.getDisplayName(this), state.getGroupTitle(), state.getSelectionCount()); - - new MaterialAlertDialogBuilder(this) - .setMessage(message) - .setNegativeButton(android.R.string.cancel, (dialog, which) -> dialog.cancel()) - .setPositiveButton(R.string.AddMembersActivity__add, (dialog, which) -> { - dialog.dismiss(); - onFinishedSelection(); - }) - .setCancelable(true) - .show(); - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersActivity.kt similarity index 97% rename from app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersActivityV2.kt rename to app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersActivity.kt index 0e981dd5a5..09b7941cc2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersActivity.kt @@ -64,7 +64,7 @@ import java.text.NumberFormat /** * Allows members to be added to an existing Signal group by selecting from a list of recipients. */ -class AddMembersActivityV2 : PassphraseRequiredActivity() { +class AddMembersActivity : PassphraseRequiredActivity() { companion object { private const val EXTRA_GROUP_ID = "group_id" private const val EXTRA_SELECTION_LIMITS = "selection_limits" @@ -74,7 +74,7 @@ class AddMembersActivityV2 : PassphraseRequiredActivity() { context: Context, event: ConversationSettingsEvent.AddMembersToGroup ): Intent { - return Intent(context, AddMembersActivityV2::class.java).apply { + return Intent(context, AddMembersActivity::class.java).apply { putExtra(EXTRA_GROUP_ID, event.groupId) putExtra(EXTRA_SELECTION_LIMITS, event.selectionLimits) putParcelableArrayListExtra(EXTRA_PRESELECTED_RECIPIENTS, ArrayList(event.groupMembersWithoutSelf)) @@ -90,7 +90,7 @@ class AddMembersActivityV2 : PassphraseRequiredActivity() { SignalTheme { AddMembersScreen( viewModel = viewModel { - AddMembersViewModelV2( + AddMembersViewModel( groupId = intent.getParcelableExtraCompat(EXTRA_GROUP_ID, GroupId::class.java)!!, existingMembersMinusSelf = intent.getParcelableArrayListExtraCompat(EXTRA_PRESELECTED_RECIPIENTS, RecipientId::class.java)!!.toSet(), selectionLimits = intent.getParcelableExtraCompat(EXTRA_SELECTION_LIMITS, SelectionLimits::class.java)!! @@ -109,7 +109,7 @@ class AddMembersActivityV2 : PassphraseRequiredActivity() { @Composable private fun AddMembersScreen( - viewModel: AddMembersViewModelV2, + viewModel: AddMembersViewModel, activityIntent: Intent, closeScreen: (result: ActivityResult) -> Unit ) { @@ -294,7 +294,7 @@ private fun GroupAddConfirmationDialog( val bodyText: String = when (message) { is UserMessage.ConfirmAddMember -> { stringResource( - id = R.string.AddMembersActivityV2__add_member_to_s, + id = R.string.AddMembersActivity__add_member_to_s, message.recipient.getDisplayName(context), message.group.getDisplayTitle(context) ) @@ -302,7 +302,7 @@ private fun GroupAddConfirmationDialog( is UserMessage.ConfirmAddMembers -> { pluralStringResource( - id = R.plurals.AddMembersActivityV2__add_d_members_to_s, + id = R.plurals.AddMembersActivity__add_d_members_to_s, message.recipientIds.size, message.recipientIds.size, message.group.getDisplayTitle(context) diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersRepository.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersRepository.java deleted file mode 100644 index 8643d0948d..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersRepository.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.thoughtcrime.securesms.groups.ui.addmembers; - -import android.content.Context; - -import androidx.annotation.NonNull; -import androidx.annotation.WorkerThread; - -import org.thoughtcrime.securesms.contacts.SelectedContact; -import org.thoughtcrime.securesms.database.SignalDatabase; -import org.thoughtcrime.securesms.dependencies.AppDependencies; -import org.thoughtcrime.securesms.groups.GroupId; -import org.thoughtcrime.securesms.recipients.RecipientId; - -final class AddMembersRepository { - - private final Context context; - private final GroupId groupId; - - AddMembersRepository(@NonNull GroupId groupId) { - this.groupId = groupId; - this.context = AppDependencies.getApplication(); - } - - @WorkerThread - RecipientId getOrCreateRecipientId(@NonNull SelectedContact selectedContact) { - return selectedContact.getOrCreateRecipientId(); - } - - @WorkerThread - String getGroupTitle() { - return SignalDatabase.groups().requireGroup(groupId).getTitle(); - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersViewModel.java deleted file mode 100644 index 4e2e5a3b8f..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersViewModel.java +++ /dev/null @@ -1,114 +0,0 @@ -package org.thoughtcrime.securesms.groups.ui.addmembers; - -import android.text.TextUtils; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.WorkerThread; -import androidx.core.util.Consumer; -import androidx.lifecycle.ViewModel; -import androidx.lifecycle.ViewModelProvider; - -import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.contacts.SelectedContact; -import org.thoughtcrime.securesms.dependencies.AppDependencies; -import org.thoughtcrime.securesms.groups.GroupId; -import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientId; -import org.signal.core.util.concurrent.SimpleTask; -import org.whispersystems.signalservice.api.util.Preconditions; - -import java.util.List; -import java.util.Objects; - -public final class AddMembersViewModel extends ViewModel { - - private final AddMembersRepository repository; - - private AddMembersViewModel(@NonNull GroupId groupId) { - this.repository = new AddMembersRepository(groupId); - } - - void getDialogStateForSelectedContacts(@NonNull List selectedContacts, - @NonNull Consumer callback) - { - SimpleTask.run( - () -> { - AddMemberDialogMessageStatePartial partialState = selectedContacts.size() == 1 ? getDialogStateForSingleRecipient(selectedContacts.get(0)) - : getDialogStateForMultipleRecipients(selectedContacts.size()); - - return new AddMemberDialogMessageState(partialState.recipientId == null ? Recipient.UNKNOWN : Recipient.resolved(partialState.recipientId), - partialState.memberCount, titleOrDefault(repository.getGroupTitle())); - }, - callback::accept - ); - } - - @WorkerThread - private AddMemberDialogMessageStatePartial getDialogStateForSingleRecipient(@NonNull SelectedContact selectedContact) { - return new AddMemberDialogMessageStatePartial(repository.getOrCreateRecipientId(selectedContact)); - } - - private AddMemberDialogMessageStatePartial getDialogStateForMultipleRecipients(int recipientCount) { - return new AddMemberDialogMessageStatePartial(recipientCount); - } - - private static @NonNull String titleOrDefault(@Nullable String title) { - return TextUtils.isEmpty(title) ? AppDependencies.getApplication().getString(R.string.Recipient_unknown) - : Objects.requireNonNull(title); - } - - private static final class AddMemberDialogMessageStatePartial { - private final RecipientId recipientId; - private final int memberCount; - - private AddMemberDialogMessageStatePartial(@NonNull RecipientId recipientId) { - this.recipientId = recipientId; - this.memberCount = 1; - } - - private AddMemberDialogMessageStatePartial(int memberCount) { - Preconditions.checkArgument(memberCount > 1); - this.memberCount = memberCount; - this.recipientId = null; - } - } - - public static final class AddMemberDialogMessageState { - private final Recipient recipient; - private final String groupTitle; - private final int selectionCount; - - private AddMemberDialogMessageState(@Nullable Recipient recipient, int selectionCount, @NonNull String groupTitle) { - this.recipient = recipient; - this.groupTitle = groupTitle; - this.selectionCount = selectionCount; - } - - public Recipient getRecipient() { - return recipient; - } - - public int getSelectionCount() { - return selectionCount; - } - - public @NonNull String getGroupTitle() { - return groupTitle; - } - } - - public static class Factory implements ViewModelProvider.Factory { - - private final GroupId groupId; - - public Factory(@NonNull GroupId groupId) { - this.groupId = groupId; - } - - @Override - public @NonNull T create(@NonNull Class modelClass) { - return Objects.requireNonNull(modelClass.cast(new AddMembersViewModel(groupId))); - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersViewModelV2.kt b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersViewModel.kt similarity index 99% rename from app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersViewModelV2.kt rename to app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersViewModel.kt index 3816e314e8..bae545a167 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersViewModelV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/addmembers/AddMembersViewModel.kt @@ -28,7 +28,7 @@ import org.thoughtcrime.securesms.recipients.RecipientRepository import org.thoughtcrime.securesms.recipients.ui.RecipientSelection import kotlin.collections.plus -class AddMembersViewModelV2( +class AddMembersViewModel( private val groupId: GroupId, existingMembersMinusSelf: Set, selectionLimits: SelectionLimits diff --git a/app/src/main/res/layout/add_members_activity.xml b/app/src/main/res/layout/add_members_activity.xml deleted file mode 100644 index 626d3ac5d2..0000000000 --- a/app/src/main/res/layout/add_members_activity.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a9298488f3..77eeb59d2c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1397,17 +1397,11 @@ Done This person can\'t be added to legacy groups. - - - Add \"%1$s\" to \"%2$s\"? - Add %3$d members to \"%2$s\"? - - - Add \"%1$s\" to \"%2$s\"? + Add \"%1$s\" to \"%2$s\"? - + Add %1$d member to \"%2$s\"? Add %1$d members to \"%2$s\"? @@ -5220,11 +5214,6 @@ Skip - - %1$d member - %1$d members - - %1$s member