mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-22 18:00:02 +01:00
Improve handling of membership changes during a GV1->GV2 migration.
This commit is contained in:
committed by
Alex Hart
parent
d4748efd42
commit
3804a89619
@@ -0,0 +1,60 @@
|
||||
package org.thoughtcrime.securesms.groups;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.StringUtil;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Describes a change in membership that results from a GV1->GV2 migration.
|
||||
*/
|
||||
public final class GroupMigrationMembershipChange {
|
||||
private final List<RecipientId> pending;
|
||||
private final List<RecipientId> dropped;
|
||||
|
||||
public GroupMigrationMembershipChange(@NonNull List<RecipientId> pending, @NonNull List<RecipientId> dropped) {
|
||||
this.pending = pending;
|
||||
this.dropped = dropped;
|
||||
}
|
||||
|
||||
public static GroupMigrationMembershipChange empty() {
|
||||
return new GroupMigrationMembershipChange(Collections.emptyList(), Collections.emptyList());
|
||||
}
|
||||
|
||||
public static @NonNull GroupMigrationMembershipChange deserialize(@Nullable String serialized) {
|
||||
if (Util.isEmpty(serialized)) {
|
||||
return empty();
|
||||
} else {
|
||||
String[] parts = serialized.split("\\|");
|
||||
if (parts.length == 1) {
|
||||
return new GroupMigrationMembershipChange(RecipientId.fromSerializedList(parts[0]), Collections.emptyList());
|
||||
} else if (parts.length == 2) {
|
||||
return new GroupMigrationMembershipChange(RecipientId.fromSerializedList(parts[0]), RecipientId.fromSerializedList(parts[1]));
|
||||
} else {
|
||||
return GroupMigrationMembershipChange.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public @NonNull List<RecipientId> getPending() {
|
||||
return pending;
|
||||
}
|
||||
|
||||
public @NonNull List<RecipientId> getDropped() {
|
||||
return dropped;
|
||||
}
|
||||
|
||||
public @NonNull String serialize() {
|
||||
return RecipientId.toSerializedList(pending) + "|" + RecipientId.toSerializedList(dropped);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return pending.isEmpty() && dropped.isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.thoughtcrime.securesms.groups.v2.processing.GroupsV2StateProcessor.LATEST;
|
||||
@@ -182,15 +183,8 @@ public final class GroupsV1MigrationUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<RecipientId> pendingRecipients = Stream.of(DecryptedGroupUtil.pendingToUuidList(decryptedGroup.getPendingMembersList()))
|
||||
.map(uuid -> Recipient.externalPush(context, uuid, null, false))
|
||||
.filterNot(Recipient::isSelf)
|
||||
.map(Recipient::getId)
|
||||
.toList();
|
||||
|
||||
Log.i(TAG, "[Local] Migrating group over to the version we were added to: V" + decryptedGroup.getRevision());
|
||||
DatabaseFactory.getGroupDatabase(context).migrateToV2(gv1Id, decryptedGroup);
|
||||
DatabaseFactory.getSmsDatabase(context).insertGroupV1MigrationEvents(groupRecipient.getId(), threadId, pendingRecipients);
|
||||
DatabaseFactory.getGroupDatabase(context).migrateToV2(threadId, gv1Id, decryptedGroup);
|
||||
|
||||
Log.i(TAG, "[Local] Applying all changes since V" + decryptedGroup.getRevision());
|
||||
try {
|
||||
|
||||
@@ -15,13 +15,12 @@ import androidx.lifecycle.ViewModelProviders;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupMemberListView;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.BottomSheetUtil;
|
||||
import org.thoughtcrime.securesms.util.ThemeUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -30,16 +29,19 @@ import java.util.List;
|
||||
*/
|
||||
public final class GroupsV1MigrationInfoBottomSheetDialogFragment extends BottomSheetDialogFragment {
|
||||
|
||||
private static final String KEY_PENDING = "pending";
|
||||
private static final String KEY_MEMBERSHIP_CHANGE = "membership_change";
|
||||
|
||||
private GroupsV1MigrationInfoViewModel viewModel;
|
||||
private GroupMemberListView pendingList;
|
||||
private TextView pendingTitle;
|
||||
private View pendingContainer;
|
||||
private GroupMemberListView droppedList;
|
||||
private TextView droppedTitle;
|
||||
private View droppedContainer;
|
||||
|
||||
public static void showForLearnMore(@NonNull FragmentManager manager, @NonNull List<RecipientId> pendingRecipients) {
|
||||
public static void show(@NonNull FragmentManager manager, @NonNull GroupMigrationMembershipChange membershipChange) {
|
||||
Bundle args = new Bundle();
|
||||
args.putParcelableArrayList(KEY_PENDING, new ArrayList<>(pendingRecipients));
|
||||
args.putString(KEY_MEMBERSHIP_CHANGE, membershipChange.serialize());
|
||||
|
||||
GroupsV1MigrationInfoBottomSheetDialogFragment fragment = new GroupsV1MigrationInfoBottomSheetDialogFragment();
|
||||
fragment.setArguments(args);
|
||||
@@ -66,12 +68,16 @@ public final class GroupsV1MigrationInfoBottomSheetDialogFragment extends Bottom
|
||||
this.pendingContainer = view.findViewById(R.id.gv1_learn_more_pending_container);
|
||||
this.pendingTitle = view.findViewById(R.id.gv1_learn_more_pending_title);
|
||||
this.pendingList = view.findViewById(R.id.gv1_learn_more_pending_list);
|
||||
this.droppedContainer = view.findViewById(R.id.gv1_learn_more_dropped_container);
|
||||
this.droppedTitle = view.findViewById(R.id.gv1_learn_more_dropped_title);
|
||||
this.droppedList = view.findViewById(R.id.gv1_learn_more_dropped_list);
|
||||
|
||||
//noinspection ConstantConditions
|
||||
List<RecipientId> pending = getArguments().getParcelableArrayList(KEY_PENDING);
|
||||
GroupMigrationMembershipChange membershipChange = GroupMigrationMembershipChange.deserialize(getArguments().getString(KEY_MEMBERSHIP_CHANGE));
|
||||
|
||||
this.viewModel = ViewModelProviders.of(this, new GroupsV1MigrationInfoViewModel.Factory(pending)).get(GroupsV1MigrationInfoViewModel.class);
|
||||
this.viewModel = ViewModelProviders.of(this, new GroupsV1MigrationInfoViewModel.Factory(membershipChange)).get(GroupsV1MigrationInfoViewModel.class);
|
||||
viewModel.getPendingMembers().observe(getViewLifecycleOwner(), this::onPendingMembersChanged);
|
||||
viewModel.getDroppedMembers().observe(getViewLifecycleOwner(), this::onDroppedMembersChanged);
|
||||
|
||||
view.findViewById(R.id.gv1_learn_more_ok_button).setOnClickListener(v -> dismiss());
|
||||
}
|
||||
@@ -82,7 +88,10 @@ public final class GroupsV1MigrationInfoBottomSheetDialogFragment extends Bottom
|
||||
}
|
||||
|
||||
private void onPendingMembersChanged(@NonNull List<Recipient> pendingMembers) {
|
||||
if (pendingMembers.size() > 0) {
|
||||
if (pendingMembers.size() == 1 && pendingMembers.get(0).isSelf()) {
|
||||
pendingContainer.setVisibility(View.VISIBLE);
|
||||
pendingTitle.setText(R.string.GroupsV1MigrationLearnMore_you_will_need_to_accept_an_invite_to_join_this_group_again);
|
||||
} else if (pendingMembers.size() > 0) {
|
||||
pendingContainer.setVisibility(View.VISIBLE);
|
||||
pendingTitle.setText(getResources().getQuantityText(R.plurals.GroupsV1MigrationLearnMore_these_members_will_need_to_accept_an_invite, pendingMembers.size()));
|
||||
pendingList.setDisplayOnlyMembers(pendingMembers);
|
||||
@@ -90,4 +99,14 @@ public final class GroupsV1MigrationInfoBottomSheetDialogFragment extends Bottom
|
||||
pendingContainer.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void onDroppedMembersChanged(@NonNull List<Recipient> droppedMembers) {
|
||||
if (droppedMembers.size() > 0) {
|
||||
droppedContainer.setVisibility(View.VISIBLE);
|
||||
droppedTitle.setText(getResources().getQuantityText(R.plurals.GroupsV1MigrationLearnMore_these_members_were_removed_from_the_group, droppedMembers.size()));
|
||||
droppedList.setDisplayOnlyMembers(droppedMembers);
|
||||
} else {
|
||||
droppedContainer.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
||||
@@ -15,12 +16,18 @@ import java.util.List;
|
||||
class GroupsV1MigrationInfoViewModel extends ViewModel {
|
||||
|
||||
private final MutableLiveData<List<Recipient>> pendingMembers;
|
||||
private final MutableLiveData<List<Recipient>> droppedMembers;
|
||||
|
||||
private GroupsV1MigrationInfoViewModel(@NonNull List<RecipientId> pendingMembers) {
|
||||
private GroupsV1MigrationInfoViewModel(@NonNull GroupMigrationMembershipChange membershipChange) {
|
||||
this.pendingMembers = new MutableLiveData<>();
|
||||
this.droppedMembers = new MutableLiveData<>();
|
||||
|
||||
SignalExecutors.BOUNDED.execute(() -> {
|
||||
this.pendingMembers.postValue(Recipient.resolvedList(pendingMembers));
|
||||
this.pendingMembers.postValue(Recipient.resolvedList(membershipChange.getPending()));
|
||||
});
|
||||
|
||||
SignalExecutors.BOUNDED.execute(() -> {
|
||||
this.droppedMembers.postValue(Recipient.resolvedList(membershipChange.getDropped()));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -28,17 +35,21 @@ class GroupsV1MigrationInfoViewModel extends ViewModel {
|
||||
return pendingMembers;
|
||||
}
|
||||
|
||||
@NonNull LiveData<List<Recipient>> getDroppedMembers() {
|
||||
return droppedMembers;
|
||||
}
|
||||
|
||||
static class Factory extends ViewModelProvider.NewInstanceFactory {
|
||||
|
||||
private final List<RecipientId> pendingMembers;
|
||||
private final GroupMigrationMembershipChange membershipChange;
|
||||
|
||||
Factory(List<RecipientId> pendingMembers) {
|
||||
this.pendingMembers = pendingMembers;
|
||||
Factory(@NonNull GroupMigrationMembershipChange membershipChange) {
|
||||
this.membershipChange = membershipChange;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull<T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
||||
return modelClass.cast(new GroupsV1MigrationInfoViewModel(pendingMembers));
|
||||
return modelClass.cast(new GroupsV1MigrationInfoViewModel(membershipChange));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user