From deb4e356bde0445aa82c151b24166f0449ce134b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Feb 2026 23:03:32 +0000 Subject: [PATCH] Fix race condition by tracking removed contacts explicitly Instead of comparing current members at result time (which can change), ContactMultiSelectionActivity now tracks preselected contacts and explicitly returns which ones were removed. This eliminates the race condition where member list changes could cause accidental removals. Changes: - ContactMultiSelectionActivity: Store preselected contacts, calculate and return removed contacts - ProfileFragment: Use explicitly removed contacts instead of calculating from current member list Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com> --- .../ContactMultiSelectionActivity.java | 21 +++++++++++++++++++ .../securesms/ProfileFragment.java | 18 +++++++--------- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/thoughtcrime/securesms/ContactMultiSelectionActivity.java b/src/main/java/org/thoughtcrime/securesms/ContactMultiSelectionActivity.java index 9654c0249..7197c4544 100644 --- a/src/main/java/org/thoughtcrime/securesms/ContactMultiSelectionActivity.java +++ b/src/main/java/org/thoughtcrime/securesms/ContactMultiSelectionActivity.java @@ -23,7 +23,9 @@ import android.view.MenuInflater; import android.view.MenuItem; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * Activity container for selecting a list of contacts. @@ -34,6 +36,9 @@ import java.util.List; public class ContactMultiSelectionActivity extends ContactSelectionActivity { public static final String CONTACTS_EXTRA = "contacts_extra"; + public static final String REMOVED_CONTACTS_EXTRA = "removed_contacts_extra"; + + private ArrayList preselectedContacts; @Override protected void onCreate(Bundle icicle, boolean ready) { @@ -44,6 +49,9 @@ public class ContactMultiSelectionActivity extends ContactSelectionActivity { // it's a bit confusing having one "X" button on the left and one on the right - // and the "clear search" button is not that important. getToolbar().setUseClearButton(false); + + // Store preselected contacts to track which ones were removed + preselectedContacts = getIntent().getIntegerArrayListExtra(ContactSelectionListFragment.PRESELECTED_CONTACTS); } @Override @@ -72,6 +80,19 @@ public class ContactMultiSelectionActivity extends ContactSelectionActivity { Intent resultIntent = getIntent(); List selectedContacts = contactsFragment.getSelectedContacts(); resultIntent.putIntegerArrayListExtra(CONTACTS_EXTRA, new ArrayList<>(selectedContacts)); + + // Calculate which contacts were removed (preselected but not in final selection) + if (preselectedContacts != null) { + Set selectedSet = new HashSet<>(selectedContacts); + ArrayList removedContacts = new ArrayList<>(); + for (Integer preselectedId : preselectedContacts) { + if (!selectedSet.contains(preselectedId)) { + removedContacts.add(preselectedId); + } + } + resultIntent.putIntegerArrayListExtra(REMOVED_CONTACTS_EXTRA, removedContacts); + } + setResult(RESULT_OK, resultIntent); } } diff --git a/src/main/java/org/thoughtcrime/securesms/ProfileFragment.java b/src/main/java/org/thoughtcrime/securesms/ProfileFragment.java index 4ba89c366..ed207f46c 100644 --- a/src/main/java/org/thoughtcrime/securesms/ProfileFragment.java +++ b/src/main/java/org/thoughtcrime/securesms/ProfileFragment.java @@ -34,9 +34,7 @@ import org.thoughtcrime.securesms.util.ViewUtil; import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; import java.util.List; -import java.util.Set; public class ProfileFragment extends Fragment implements ProfileAdapter.ItemClickListener, DcEventCenter.DcEventDelegate { @@ -312,11 +310,9 @@ public class ProfileFragment extends Fragment super.onActivityResult(requestCode, resultCode, data); if (requestCode==REQUEST_CODE_PICK_CONTACT && resultCode==Activity.RESULT_OK && data!=null) { List selected = data.getIntegerArrayListExtra(ContactMultiSelectionActivity.CONTACTS_EXTRA); + List removed = data.getIntegerArrayListExtra(ContactMultiSelectionActivity.REMOVED_CONTACTS_EXTRA); if(selected == null) return; Util.runOnAnyBackgroundThread(() -> { - // Get current members - int[] currentMembers = dcContext.getChatContacts(chatId); - // Add new members for (Integer contactId : selected) { if (contactId != null) { @@ -324,12 +320,12 @@ public class ProfileFragment extends Fragment } } - // Remove members that were unchecked - // Use HashSet for O(1) lookup performance - Set selectedSet = new HashSet<>(selected); - for (int currentMemberId : currentMembers) { - if (!selectedSet.contains(currentMemberId)) { - dcContext.removeContactFromChat(chatId, currentMemberId); + // Remove members that were explicitly unchecked + if (removed != null) { + for (Integer contactId : removed) { + if (contactId != null) { + dcContext.removeContactFromChat(chatId, contactId); + } } } });