Remove GV2 capability check.

This commit is contained in:
Greyson Parrelli
2022-03-09 17:01:00 -05:00
committed by Cody Henthorne
parent d13d8628b5
commit c1181478dd
20 changed files with 29 additions and 318 deletions

View File

@@ -329,7 +329,7 @@ final class GroupManagerV2 {
@NonNull GroupManager.GroupActionResult addMembers(@NonNull Collection<RecipientId> newMembers)
throws GroupChangeFailedException, GroupInsufficientRightsException, IOException, GroupNotAMemberException, MembershipNotSuitableForV2Exception
{
if (!GroupsV2CapabilityChecker.allHaveUuidAndSupportGroupsV2(newMembers)) {
if (!GroupsV2CapabilityChecker.allHaveServiceId(newMembers)) {
throw new MembershipNotSuitableForV2Exception("At least one potential new member does not support GV2 or UUID capabilities");
}
@@ -736,7 +736,7 @@ final class GroupManagerV2 {
int disappearingMessageTimerSeconds)
throws GroupChangeFailedException, IOException, MembershipNotSuitableForV2Exception, GroupAlreadyExistsException
{
if (!GroupsV2CapabilityChecker.allAndSelfHaveUuidAndSupportGroupsV2(members)) {
if (!GroupsV2CapabilityChecker.allAndSelfHaveServiceId(members)) {
throw new MembershipNotSuitableForV2Exception("At least one potential new member does not support GV2 capability or we don't have their UUID");
}
@@ -976,7 +976,7 @@ final class GroupManagerV2 {
private @NonNull GroupChange joinGroupOnServer(boolean requestToJoin, int currentRevision)
throws GroupChangeFailedException, IOException, MembershipNotSuitableForV2Exception, GroupLinkNotActiveException, GroupJoinAlreadyAMemberException
{
if (!GroupsV2CapabilityChecker.allAndSelfHaveUuidAndSupportGroupsV2(Collections.singleton(Recipient.self().getId()))) {
if (!GroupsV2CapabilityChecker.allAndSelfHaveServiceId(Collections.singleton(Recipient.self().getId()))) {
throw new MembershipNotSuitableForV2Exception("Self does not support GV2 or UUID capabilities");
}

View File

@@ -223,8 +223,7 @@ public final class GroupsV1MigrationUtil {
*/
private static @NonNull List<Recipient> getMigratableManualMigrationMembers(@NonNull List<Recipient> registeredMembers) {
return Stream.of(registeredMembers)
.filter(r -> r.getGroupsV2Capability() == Recipient.Capability.SUPPORTED &&
r.getGroupsV1MigrationCapability() == Recipient.Capability.SUPPORTED)
.filter(r -> r.getGroupsV1MigrationCapability() == Recipient.Capability.SUPPORTED)
.toList();
}
@@ -233,7 +232,6 @@ public final class GroupsV1MigrationUtil {
*/
public static boolean isAutoMigratable(@NonNull Recipient recipient) {
return recipient.hasServiceId() &&
recipient.getGroupsV2Capability() == Recipient.Capability.SUPPORTED &&
recipient.getGroupsV1MigrationCapability() == Recipient.Capability.SUPPORTED &&
recipient.getRegistered() == RecipientDatabase.RegisteredState.REGISTERED &&
recipient.getProfileKey() != null;

View File

@@ -27,88 +27,19 @@ public final class GroupsV2CapabilityChecker {
private GroupsV2CapabilityChecker() {}
/**
* @param resolved A collection of resolved recipients.
* @return True if a recipient needed to be refreshed, otherwise false.
*/
@WorkerThread
public static boolean refreshCapabilitiesIfNecessary(@NonNull Collection<Recipient> resolved) throws IOException {
Set<RecipientId> needsRefresh = Stream.of(resolved)
.filter(r -> r.getGroupsV2Capability() != Recipient.Capability.SUPPORTED)
.map(Recipient::getId)
.collect(Collectors.toSet());
if (needsRefresh.size() > 0) {
Log.d(TAG, "[refreshCapabilitiesIfNecessary] Need to refresh " + needsRefresh.size() + " recipients.");
List<Job> jobs = RetrieveProfileJob.forRecipients(needsRefresh);
JobManager jobManager = ApplicationDependencies.getJobManager();
for (Job job : jobs) {
if (!jobManager.runSynchronously(job, TimeUnit.SECONDS.toMillis(10)).isPresent()) {
throw new IOException("Recipient capability was not retrieved in time");
}
}
return true;
} else {
return false;
}
}
@WorkerThread
static boolean allAndSelfHaveUuidAndSupportGroupsV2(@NonNull Collection<RecipientId> recipientIds)
throws IOException
{
static boolean allAndSelfHaveServiceId(@NonNull Collection<RecipientId> recipientIds) {
HashSet<RecipientId> recipientIdsSet = new HashSet<>(recipientIds);
recipientIdsSet.add(Recipient.self().getId());
return allHaveUuidAndSupportGroupsV2(recipientIdsSet);
return allHaveServiceId(recipientIdsSet);
}
@WorkerThread
static boolean allHaveUuidAndSupportGroupsV2(@NonNull Collection<RecipientId> recipientIds)
throws IOException
{
Set<RecipientId> recipientIdsSet = new HashSet<>(recipientIds);
refreshCapabilitiesIfNecessary(Recipient.resolvedList(recipientIdsSet));
boolean noSelfGV2Support = false;
int noGv2Count = 0;
int noUuidCount = 0;
for (RecipientId id : recipientIds) {
Recipient member = Recipient.resolved(id);
Recipient.Capability gv2Capability = member.getGroupsV2Capability();
if (gv2Capability != Recipient.Capability.SUPPORTED) {
Log.w(TAG, "At least one recipient does not support GV2, capability was " + gv2Capability);
noGv2Count++;
if (member.isSelf()) {
noSelfGV2Support = true;
}
}
if (!member.hasServiceId()) {
noUuidCount++;
}
}
if (noGv2Count + noUuidCount > 0) {
if (noUuidCount > 0) {
Log.w(TAG, noUuidCount + " recipient(s) did not have a UUID known to us");
}
if (noGv2Count > 0) {
Log.w(TAG, noGv2Count + " recipient(s) do not support GV2");
if (noSelfGV2Support) {
Log.w(TAG, "Self does not support GV2");
}
}
return false;
}
return true;
static boolean allHaveServiceId(@NonNull Collection<RecipientId> recipientIds) {
return Recipient.resolvedList(recipientIds)
.stream()
.allMatch(Recipient::hasServiceId);
}
}

View File

@@ -4,14 +4,11 @@ import android.animation.ValueAnimator;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Pair;
import android.view.MenuItem;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import com.annimon.stream.Stream;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton;
@@ -22,9 +19,7 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.groups.GroupsV2CapabilityChecker;
import org.thoughtcrime.securesms.groups.ui.creategroup.details.AddGroupDetailsActivity;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.FeatureFlags;
@@ -36,8 +31,6 @@ import org.thoughtcrime.securesms.util.views.SimpleProgressDialog;
import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
@@ -189,60 +182,13 @@ public class CreateGroupActivity extends ContactSelectionActivity {
}
}
if (registeredChecks.size() > 0) {
resolved = Recipient.resolvedList(ids);
}
stopwatch.split("registered");
List<Recipient> recipientsAndSelf = new ArrayList<>(resolved);
recipientsAndSelf.add(Recipient.self().resolve());
boolean neededRefresh = false;
if (!SignalStore.internalValues().gv2DoNotCreateGv2Groups()) {
try {
neededRefresh = GroupsV2CapabilityChecker.refreshCapabilitiesIfNecessary(recipientsAndSelf);
} catch (IOException e) {
Log.w(TAG, "Failed to refresh all recipient capabilities.", e);
}
}
if (neededRefresh) {
resolved = Recipient.resolvedList(ids);
}
stopwatch.split("capabilities");
Pair<Boolean, List<RecipientId>> result;
boolean gv2 = Stream.of(recipientsAndSelf).allMatch(r -> r.getGroupsV2Capability() == Recipient.Capability.SUPPORTED);
if (!gv2 && Stream.of(resolved).anyMatch(r -> !r.hasE164()))
{
Log.w(TAG, "Invalid GV1 group...");
ids = Collections.emptyList();
result = Pair.create(false, ids);
} else {
result = Pair.create(true, ids);
}
stopwatch.split("gv1-check");
return result;
}, result -> {
return ids;
}, recipientIds -> {
dismissibleDialog.dismiss();
stopwatch.stop(TAG);
if (result.first) {
startActivityForResult(AddGroupDetailsActivity.newIntent(this, result.second), REQUEST_CODE_ADD_DETAILS);
} else {
new AlertDialog.Builder(this)
.setMessage(R.string.CreateGroupActivity_some_contacts_cannot_be_in_legacy_groups)
.setPositiveButton(android.R.string.ok, (d, w) -> d.dismiss())
.show();
}
startActivityForResult(AddGroupDetailsActivity.newIntent(this, recipientIds), REQUEST_CODE_ADD_DETAILS);
});
}
}

View File

@@ -35,9 +35,7 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.avatar.picker.AvatarPickerFragment;
import org.thoughtcrime.securesms.components.settings.app.privacy.expire.ExpireTimerSettingsFragment;
import org.thoughtcrime.securesms.groups.ui.GroupMemberListView;
import org.thoughtcrime.securesms.groups.ui.creategroup.dialogs.NonGv2MemberDialog;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.mediasend.AvatarSelectionActivity;
import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
import org.thoughtcrime.securesms.mms.GlideApp;
@@ -131,12 +129,6 @@ public class AddGroupDetailsFragment extends LoggingFragment {
name.setHint(isMms ? R.string.AddGroupDetailsFragment__group_name_optional : R.string.AddGroupDetailsFragment__group_name_required);
toolbar.setTitle(isMms ? R.string.AddGroupDetailsFragment__create_group : R.string.AddGroupDetailsFragment__name_this_group);
});
viewModel.getNonGv2CapableMembers().observe(getViewLifecycleOwner(), nonGv2CapableMembers -> {
gv2Warning.setVisibility(nonGv2CapableMembers.isEmpty() ? View.GONE : View.VISIBLE);
gv2Warning.setText(requireContext().getResources().getQuantityString(R.plurals.AddGroupDetailsFragment__d_members_do_not_support_new_groups_so_this_group_cannot_be_created, nonGv2CapableMembers.size(), nonGv2CapableMembers.size()));
gv2Warning.setLearnMoreVisible(true);
gv2Warning.setOnLinkClickListener(v -> NonGv2MemberDialog.showNonGv2Members(requireContext(), getViewLifecycleOwner(), nonGv2CapableMembers));
});
viewModel.getAvatar().observe(getViewLifecycleOwner(), avatarBytes -> {
if (avatarBytes == null) {
avatar.setImageDrawable(new InsetDrawable(avatarPlaceholder, ViewUtil.dpToPx(AVATAR_PLACEHOLDER_INSET_DP)));

View File

@@ -78,17 +78,4 @@ final class AddGroupDetailsRepository {
}
});
}
@WorkerThread
List<Recipient> checkCapabilities(@NonNull Collection<RecipientId> newPotentialMemberList) {
try {
GroupsV2CapabilityChecker.refreshCapabilitiesIfNecessary(Recipient.resolvedList(newPotentialMemberList));
} catch (IOException e) {
Log.w(TAG, "Could not get latest profiles for users, using known gv2 capability state", e);
}
return Stream.of(Recipient.resolvedList(newPotentialMemberList))
.filter(m -> m.getGroupsV2Capability() != Recipient.Capability.SUPPORTED)
.toList();
}
}

View File

@@ -41,7 +41,6 @@ public final class AddGroupDetailsViewModel extends ViewModel {
private final LiveData<Boolean> isMms;
private final LiveData<Boolean> canSubmitForm;
private final AddGroupDetailsRepository repository;
private final LiveData<List<Recipient>> nonGv2CapableMembers;
private Media avatarMedia;
@@ -64,10 +63,7 @@ public final class AddGroupDetailsViewModel extends ViewModel {
}
});
nonGv2CapableMembers = LiveDataUtil.mapAsync(membersToCheckGv2CapabilityOf, memberList -> repository.checkCapabilities(Stream.of(memberList).map(newGroupCandidate -> newGroupCandidate.getMember().getId()).toList()));
canSubmitForm = LiveDataUtil.combineLatest(LiveDataUtil.combineLatest(isMms, isValidName, (mms, validName) -> mms || validName),
nonGv2CapableMembers,
(canSubmit, nonGv2) -> canSubmit && nonGv2.isEmpty());
canSubmitForm = LiveDataUtil.combineLatest(isMms, isValidName, (mms, validName) -> mms || validName);
repository.resolveMembers(recipientIds, initialMembers::postValue);
}
@@ -92,10 +88,6 @@ public final class AddGroupDetailsViewModel extends ViewModel {
return isMms;
}
@NonNull LiveData<List<Recipient>> getNonGv2CapableMembers() {
return nonGv2CapableMembers;
}
@NonNull LiveData<Integer> getDisappearingMessagesTimer() {
return disappearingMessagesTimer;
}

View File

@@ -1,59 +0,0 @@
package org.thoughtcrime.securesms.groups.ui.creategroup.dialogs;
import android.app.Dialog;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.LifecycleOwner;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry;
import org.thoughtcrime.securesms.groups.ui.GroupMemberListView;
import org.thoughtcrime.securesms.recipients.Recipient;
import java.util.ArrayList;
import java.util.List;
public final class NonGv2MemberDialog {
private NonGv2MemberDialog() {
}
public static @Nullable Dialog showNonGv2Members(@NonNull Context context, @NonNull LifecycleOwner lifecycleOwner, @NonNull List<Recipient> recipients) {
int size = recipients.size();
if (size == 0) {
return null;
}
AlertDialog.Builder builder = new AlertDialog.Builder(context)
// TODO: GV2 Need a URL for learn more
// .setNegativeButton(R.string.NonGv2MemberDialog_learn_more, (dialog, which) -> {
// })
.setPositiveButton(android.R.string.ok, null);
if (size == 1) {
builder.setMessage(context.getString(R.string.NonGv2MemberDialog_single_users_are_non_gv2_capable_forced_migration, recipients.get(0).getDisplayName(context)));
} else {
builder.setMessage(context.getResources().getQuantityString(R.plurals.NonGv2MemberDialog_d_users_are_non_gv2_capable_forced_migration, size, size))
.setView(R.layout.dialog_multiple_members_non_gv2_capable);
}
Dialog dialog = builder.show();
if (size > 1) {
GroupMemberListView nonGv2CapableMembers = dialog.findViewById(R.id.list_non_gv2_members);
nonGv2CapableMembers.initializeAdapter(lifecycleOwner);
List<GroupMemberEntry.NewGroupCandidate> pendingMembers = new ArrayList<>(recipients.size());
for (Recipient r : recipients) {
pendingMembers.add(new GroupMemberEntry.NewGroupCandidate(r));
}
//noinspection ConstantConditions
nonGv2CapableMembers.setMembers(pendingMembers);
}
return dialog;
}
}

View File

@@ -110,26 +110,15 @@ public final class GroupJoinBottomSheetDialogFragment extends BottomSheetDialogF
updateGroupDescription(details.getGroupName(), details.getGroupDescription());
}
switch (getGroupJoinStatus()) {
case UPDATE_LINKED_DEVICE_TO_JOIN:
groupJoinExplain.setText(R.string.GroupJoinUpdateRequiredBottomSheetDialogFragment_update_linked_device_message);
groupCancelButton.setText(android.R.string.ok);
groupJoinButton.setVisibility(View.GONE);
ApplicationDependencies.getJobManager()
.add(RetrieveProfileJob.forRecipient(Recipient.self().getId()));
break;
case LOCAL_CAN_JOIN:
groupJoinExplain.setText(details.joinRequiresAdminApproval() ? R.string.GroupJoinBottomSheetDialogFragment_admin_approval_needed
: R.string.GroupJoinBottomSheetDialogFragment_direct_join);
groupJoinButton.setText(details.joinRequiresAdminApproval() ? R.string.GroupJoinBottomSheetDialogFragment_request_to_join
: R.string.GroupJoinBottomSheetDialogFragment_join);
groupJoinButton.setOnClickListener(v -> {
Log.i(TAG, details.joinRequiresAdminApproval() ? "Attempting to direct join group" : "Attempting to request to join group");
viewModel.join(details);
});
groupJoinButton.setVisibility(View.VISIBLE);
break;
}
groupJoinExplain.setText(details.joinRequiresAdminApproval() ? R.string.GroupJoinBottomSheetDialogFragment_admin_approval_needed
: R.string.GroupJoinBottomSheetDialogFragment_direct_join);
groupJoinButton.setText(details.joinRequiresAdminApproval() ? R.string.GroupJoinBottomSheetDialogFragment_request_to_join
: R.string.GroupJoinBottomSheetDialogFragment_join);
groupJoinButton.setOnClickListener(v -> {
Log.i(TAG, details.joinRequiresAdminApproval() ? "Attempting to direct join group" : "Attempting to request to join group");
viewModel.join(details);
});
groupJoinButton.setVisibility(View.VISIBLE);
avatar.setImageBytesForGroup(details.getAvatarBytes(), new FallbackPhotoProvider(), AvatarColor.UNKNOWN);
@@ -167,14 +156,6 @@ public final class GroupJoinBottomSheetDialogFragment extends BottomSheetDialogF
() -> GroupDescriptionDialog.show(getChildFragmentManager(), name, description, true));
}
private static ExtendedGroupJoinStatus getGroupJoinStatus() {
if (Recipient.self().getGroupsV2Capability() != Recipient.Capability.SUPPORTED) {
return ExtendedGroupJoinStatus.UPDATE_LINKED_DEVICE_TO_JOIN;
} else {
return ExtendedGroupJoinStatus.LOCAL_CAN_JOIN;
}
}
private @NonNull String errorToMessage(@NonNull FetchGroupDetailsError error) {
if (error == FetchGroupDetailsError.GroupLinkNotActive) {
return getString(R.string.GroupJoinBottomSheetDialogFragment_this_group_link_is_not_active);
@@ -210,12 +191,4 @@ public final class GroupJoinBottomSheetDialogFragment extends BottomSheetDialogF
return new ResourceContactPhoto(R.drawable.ic_group_outline_48);
}
}
public enum ExtendedGroupJoinStatus {
/** Locally we're using a version that can use group links, but one or more linked devices needs updating for GV2. */
UPDATE_LINKED_DEVICE_TO_JOIN,
/** This version of the client allows joining via GV2 group links. */
LOCAL_CAN_JOIN
}
}

View File

@@ -69,8 +69,7 @@ final class GroupsV1MigrationRepository {
}
Set<RecipientId> needsRefresh = Stream.of(group.getParticipants())
.filter(r -> r.getGroupsV2Capability() != Recipient.Capability.SUPPORTED ||
r.getGroupsV1MigrationCapability() != Recipient.Capability.SUPPORTED)
.filter(r -> r.getGroupsV1MigrationCapability() != Recipient.Capability.SUPPORTED)
.map(Recipient::getId)
.collect(Collectors.toSet());
@@ -96,7 +95,6 @@ final class GroupsV1MigrationRepository {
List<Recipient> ineligible = Stream.of(group.getParticipants())
.filter(r -> !r.hasServiceId() ||
r.getGroupsV2Capability() != Recipient.Capability.SUPPORTED ||
r.getGroupsV1MigrationCapability() != Recipient.Capability.SUPPORTED ||
r.getRegistered() != RecipientDatabase.RegisteredState.REGISTERED)
.toList();