mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-26 04:33:36 +00:00
Prevent rejected/kicked group members from joining again via group link.
This commit is contained in:
@@ -12,9 +12,11 @@ import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo;
|
||||
import org.signal.zkgroup.VerificationFailedException;
|
||||
import org.signal.zkgroup.groups.GroupMasterKey;
|
||||
import org.signal.zkgroup.groups.GroupSecretParams;
|
||||
import org.signal.zkgroup.groups.UuidCiphertext;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupInviteLinkUrl;
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupLinkPassword;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
@@ -22,9 +24,11 @@ import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException;
|
||||
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -157,11 +161,11 @@ public final class GroupManager {
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public static void ejectFromGroup(@NonNull Context context, @NonNull GroupId.V2 groupId, @NonNull Recipient recipient)
|
||||
public static void ejectAndBanFromGroup(@NonNull Context context, @NonNull GroupId.V2 groupId, @NonNull Recipient recipient)
|
||||
throws GroupChangeBusyException, GroupChangeFailedException, GroupInsufficientRightsException, GroupNotAMemberException, IOException
|
||||
{
|
||||
try (GroupManagerV2.GroupEditor edit = new GroupManagerV2(context).edit(groupId.requireV2())) {
|
||||
edit.ejectMember(recipient.requireServiceId(), false);
|
||||
edit.ejectMember(recipient.requireServiceId(), false, true);
|
||||
Log.i(TAG, "Member removed from group " + groupId);
|
||||
}
|
||||
}
|
||||
@@ -273,6 +277,28 @@ public final class GroupManager {
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public static void ban(@NonNull Context context,
|
||||
@NonNull GroupId.V2 groupId,
|
||||
@NonNull RecipientId recipientId)
|
||||
throws GroupChangeBusyException, IOException, GroupChangeFailedException, GroupNotAMemberException, GroupInsufficientRightsException
|
||||
{
|
||||
try (GroupManagerV2.GroupEditor editor = new GroupManagerV2(context).edit(groupId.requireV2())) {
|
||||
editor.ban(Collections.singleton(Recipient.resolved(recipientId).requireServiceId().uuid()));
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public static void unban(@NonNull Context context,
|
||||
@NonNull GroupId.V2 groupId,
|
||||
@NonNull RecipientId recipientId)
|
||||
throws GroupChangeBusyException, IOException, GroupChangeFailedException, GroupNotAMemberException, GroupInsufficientRightsException
|
||||
{
|
||||
try (GroupManagerV2.GroupEditor editor = new GroupManagerV2(context).edit(groupId.requireV2())) {
|
||||
editor.unban(Collections.singleton(Recipient.resolved(recipientId).requireServiceId().uuid()));
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public static void applyMembershipAdditionRightsChange(@NonNull Context context,
|
||||
@NonNull GroupId.V2 groupId,
|
||||
|
||||
@@ -428,7 +428,7 @@ final class GroupManagerV2 {
|
||||
.map(r -> Recipient.resolved(r).requireServiceId().uuid())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
return commitChangeWithConflictResolution(groupOperations.createRefuseGroupJoinRequest(uuids));
|
||||
return commitChangeWithConflictResolution(groupOperations.createRefuseGroupJoinRequest(uuids, true));
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
@@ -455,15 +455,15 @@ final class GroupManagerV2 {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
} else {
|
||||
return ejectMember(ServiceId.from(selfAci.uuid()), true);
|
||||
return ejectMember(ServiceId.from(selfAci.uuid()), true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
@NonNull GroupManager.GroupActionResult ejectMember(@NonNull ServiceId serviceId, boolean allowWhenBlocked)
|
||||
@NonNull GroupManager.GroupActionResult ejectMember(@NonNull ServiceId serviceId, boolean allowWhenBlocked, boolean ban)
|
||||
throws GroupChangeFailedException, GroupInsufficientRightsException, IOException, GroupNotAMemberException
|
||||
{
|
||||
return commitChangeWithConflictResolution(groupOperations.createRemoveMembersChange(Collections.singleton(serviceId.uuid())), allowWhenBlocked);
|
||||
return commitChangeWithConflictResolution(groupOperations.createRemoveMembersChange(Collections.singleton(serviceId.uuid()), ban), allowWhenBlocked);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
@@ -530,6 +530,14 @@ final class GroupManagerV2 {
|
||||
return commitChangeWithConflictResolution(groupOperations.createAcceptInviteChange(groupCandidate.getProfileKeyCredential().get()));
|
||||
}
|
||||
|
||||
public GroupManager.GroupActionResult ban(Set<UUID> uuids) throws GroupChangeFailedException, GroupNotAMemberException, GroupInsufficientRightsException, IOException {
|
||||
return commitChangeWithConflictResolution(groupOperations.createBanUuidsChange(uuids));
|
||||
}
|
||||
|
||||
public GroupManager.GroupActionResult unban(Set<UUID> uuids) throws GroupChangeFailedException, GroupNotAMemberException, GroupInsufficientRightsException, IOException {
|
||||
return commitChangeWithConflictResolution(groupOperations.createUnbanUuidsChange(uuids));
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public GroupManager.GroupActionResult cycleGroupLinkPassword()
|
||||
throws GroupChangeFailedException, GroupNotAMemberException, GroupInsufficientRightsException, IOException
|
||||
@@ -1094,7 +1102,7 @@ final class GroupManagerV2 {
|
||||
|
||||
GroupChange signedGroupChange;
|
||||
try {
|
||||
signedGroupChange = commitCancelChangeWithConflictResolution(groupOperations.createRefuseGroupJoinRequest(uuids));
|
||||
signedGroupChange = commitCancelChangeWithConflictResolution(groupOperations.createRefuseGroupJoinRequest(uuids, false));
|
||||
} catch (GroupLinkNotActiveException e) {
|
||||
Log.d(TAG, "Unexpected unable to leave group due to group link off");
|
||||
throw new GroupChangeFailedException(e);
|
||||
|
||||
@@ -5,6 +5,8 @@ import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
|
||||
@@ -13,50 +15,37 @@ final class RequestConfirmationDialog {
|
||||
private RequestConfirmationDialog() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that you want to approve or deny a request to join the group depending on
|
||||
* {@param approve}.
|
||||
*/
|
||||
static AlertDialog show(@NonNull Context context,
|
||||
@NonNull Recipient requester,
|
||||
boolean approve,
|
||||
@NonNull Runnable onApproveOrDeny)
|
||||
{
|
||||
if (approve) {
|
||||
return showRequestApproveConfirmationDialog(context, requester, onApproveOrDeny);
|
||||
} else {
|
||||
return showRequestDenyConfirmationDialog(context, requester, onApproveOrDeny);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that you want to approve a request to join the group.
|
||||
*/
|
||||
private static AlertDialog showRequestApproveConfirmationDialog(@NonNull Context context,
|
||||
@NonNull Recipient requester,
|
||||
@NonNull Runnable onApprove)
|
||||
public static AlertDialog showApprove(@NonNull Context context,
|
||||
@NonNull Recipient requester,
|
||||
@NonNull Runnable onApprove)
|
||||
{
|
||||
return new AlertDialog.Builder(context)
|
||||
.setMessage(context.getString(R.string.RequestConfirmationDialog_add_s_to_the_group,
|
||||
requester.getDisplayName(context)))
|
||||
.setPositiveButton(R.string.RequestConfirmationDialog_add, (dialog, which) -> onApprove.run())
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show();
|
||||
return new MaterialAlertDialogBuilder(context)
|
||||
.setMessage(context.getString(R.string.RequestConfirmationDialog_add_s_to_the_group,
|
||||
requester.getDisplayName(context)))
|
||||
.setPositiveButton(R.string.RequestConfirmationDialog_add, (dialog, which) -> onApprove.run())
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that you want to deny a request to join the group.
|
||||
*/
|
||||
private static AlertDialog showRequestDenyConfirmationDialog(@NonNull Context context,
|
||||
@NonNull Recipient requester,
|
||||
@NonNull Runnable onDeny)
|
||||
public static AlertDialog showDeny(@NonNull Context context,
|
||||
@NonNull Recipient requester,
|
||||
boolean linkEnabled,
|
||||
@NonNull Runnable onDeny)
|
||||
{
|
||||
return new AlertDialog.Builder(context)
|
||||
.setMessage(context.getString(R.string.RequestConfirmationDialog_deny_request_from_s,
|
||||
requester.getDisplayName(context)))
|
||||
.setPositiveButton(R.string.RequestConfirmationDialog_deny, (dialog, which) -> onDeny.run())
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show();
|
||||
String message = linkEnabled ? context.getString(R.string.RequestConfirmationDialog_deny_request_from_s_they_will_not_be_able_to_request, requester.getDisplayName(context))
|
||||
: context.getString(R.string.RequestConfirmationDialog_deny_request_from_s, requester.getDisplayName(context));
|
||||
|
||||
return new MaterialAlertDialogBuilder(context)
|
||||
.setMessage(message)
|
||||
.setPositiveButton(R.string.RequestConfirmationDialog_deny, (dialog, which) -> onDeny.run())
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,13 +15,11 @@ import org.thoughtcrime.securesms.groups.LiveGroup;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupErrors;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupLinkUrlAndStatus;
|
||||
import org.thoughtcrime.securesms.util.AsynchronousCallback;
|
||||
import org.thoughtcrime.securesms.util.SingleLiveEvent;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class RequestingMemberInvitesViewModel extends ViewModel {
|
||||
|
||||
@@ -29,6 +27,7 @@ public class RequestingMemberInvitesViewModel extends ViewModel {
|
||||
private final RequestingMemberRepository requestingMemberRepository;
|
||||
private final MutableLiveData<String> toasts;
|
||||
private final LiveData<List<GroupMemberEntry.RequestingMember>> requesting;
|
||||
private final LiveData<GroupLinkUrlAndStatus> inviteLink;
|
||||
|
||||
private RequestingMemberInvitesViewModel(@NonNull Context context,
|
||||
@NonNull GroupId.V2 groupId,
|
||||
@@ -36,62 +35,58 @@ public class RequestingMemberInvitesViewModel extends ViewModel {
|
||||
{
|
||||
this.context = context;
|
||||
this.requestingMemberRepository = requestingMemberRepository;
|
||||
this.requesting = new LiveGroup(groupId).getRequestingMembers();
|
||||
this.toasts = new SingleLiveEvent<>();
|
||||
|
||||
LiveGroup liveGroup = new LiveGroup(groupId);
|
||||
|
||||
this.requesting = liveGroup.getRequestingMembers();
|
||||
this.inviteLink = liveGroup.getGroupLink();
|
||||
}
|
||||
|
||||
LiveData<List<GroupMemberEntry.RequestingMember>> getRequesting() {
|
||||
return requesting;
|
||||
}
|
||||
|
||||
LiveData<GroupLinkUrlAndStatus> getInviteLink() {
|
||||
return inviteLink;
|
||||
}
|
||||
|
||||
LiveData<String> getToasts() {
|
||||
return toasts;
|
||||
}
|
||||
|
||||
void approveRequestFor(@NonNull GroupMemberEntry.RequestingMember requestingMember) {
|
||||
approveOrDeny(requestingMember, true);
|
||||
requestingMember.setBusy(true);
|
||||
requestingMemberRepository.approveRequest(requestingMember.getRequester(), new AsynchronousCallback.WorkerThread<Void, GroupChangeFailureReason>() {
|
||||
@Override
|
||||
public void onComplete(@Nullable Void result) {
|
||||
requestingMember.setBusy(false);
|
||||
toasts.postValue(context.getString(R.string.RequestingMembersFragment_added_s, requestingMember.getRequester().getDisplayName(context)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@Nullable GroupChangeFailureReason error) {
|
||||
requestingMember.setBusy(false);
|
||||
toasts.postValue(context.getString(GroupErrors.getUserDisplayMessage(error)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void denyRequestFor(@NonNull GroupMemberEntry.RequestingMember requestingMember) {
|
||||
approveOrDeny(requestingMember, false);
|
||||
}
|
||||
requestingMember.setBusy(true);
|
||||
requestingMemberRepository.denyRequest(requestingMember.getRequester(), new AsynchronousCallback.WorkerThread<Void, GroupChangeFailureReason>() {
|
||||
@Override
|
||||
public void onComplete(@Nullable Void result) {
|
||||
requestingMember.setBusy(false);
|
||||
toasts.postValue(context.getString(R.string.RequestingMembersFragment_denied_s, requestingMember.getRequester().getDisplayName(context)));
|
||||
}
|
||||
|
||||
private void approveOrDeny(@NonNull GroupMemberEntry.RequestingMember requestingMember, boolean approve) {
|
||||
RequestConfirmationDialog.show(context, requestingMember.getRequester(), approve, () -> {
|
||||
Set<RecipientId> memberAsSet = Collections.singleton(requestingMember.getRequester().getId());
|
||||
|
||||
if (approve) {
|
||||
requestingMember.setBusy(true);
|
||||
requestingMemberRepository.approveRequests(memberAsSet, new AsynchronousCallback.WorkerThread<Void, GroupChangeFailureReason>() {
|
||||
@Override
|
||||
public void onComplete(@Nullable Void result) {
|
||||
requestingMember.setBusy(false);
|
||||
toasts.postValue(context.getString(R.string.RequestingMembersFragment_added_s, requestingMember.getRequester().getDisplayName(context)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@Nullable GroupChangeFailureReason error) {
|
||||
requestingMember.setBusy(false);
|
||||
toasts.postValue(context.getString(GroupErrors.getUserDisplayMessage(error)));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
requestingMember.setBusy(true);
|
||||
requestingMemberRepository.denyRequests(memberAsSet, new AsynchronousCallback.WorkerThread<Void, GroupChangeFailureReason>() {
|
||||
@Override
|
||||
public void onComplete(@Nullable Void result) {
|
||||
requestingMember.setBusy(false);
|
||||
toasts.postValue(context.getString(R.string.RequestingMembersFragment_denied_s, requestingMember.getRequester().getDisplayName(context)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@Nullable GroupChangeFailureReason error) {
|
||||
requestingMember.setBusy(false);
|
||||
toasts.postValue(context.getString(GroupErrors.getUserDisplayMessage(error)));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void onError(@Nullable GroupChangeFailureReason error) {
|
||||
requestingMember.setBusy(false);
|
||||
toasts.postValue(context.getString(GroupErrors.getUserDisplayMessage(error)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static class Factory implements ViewModelProvider.Factory {
|
||||
|
||||
@@ -10,11 +10,13 @@ import org.thoughtcrime.securesms.groups.GroupChangeException;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.groups.GroupManager;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.AsynchronousCallback;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* Repository for modifying the requesting members on a single group.
|
||||
@@ -31,12 +33,12 @@ final class RequestingMemberRepository {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
void approveRequests(@NonNull Collection<RecipientId> recipientIds,
|
||||
@NonNull AsynchronousCallback.WorkerThread<Void, GroupChangeFailureReason> callback)
|
||||
void approveRequest(@NonNull Recipient recipient,
|
||||
@NonNull AsynchronousCallback.WorkerThread<Void, GroupChangeFailureReason> callback)
|
||||
{
|
||||
SignalExecutors.UNBOUNDED.execute(() -> {
|
||||
try {
|
||||
GroupManager.approveRequests(context, groupId, recipientIds);
|
||||
GroupManager.approveRequests(context, groupId, Collections.singleton(recipient.getId()));
|
||||
callback.onComplete(null);
|
||||
} catch (GroupChangeException | IOException e) {
|
||||
Log.w(TAG, e);
|
||||
@@ -45,12 +47,12 @@ final class RequestingMemberRepository {
|
||||
});
|
||||
}
|
||||
|
||||
void denyRequests(@NonNull Collection<RecipientId> recipientIds,
|
||||
@NonNull AsynchronousCallback.WorkerThread<Void, GroupChangeFailureReason> callback)
|
||||
void denyRequest(@NonNull Recipient recipient,
|
||||
@NonNull AsynchronousCallback.WorkerThread<Void, GroupChangeFailureReason> callback)
|
||||
{
|
||||
SignalExecutors.UNBOUNDED.execute(() -> {
|
||||
try {
|
||||
GroupManager.denyRequests(context, groupId, recipientIds);
|
||||
GroupManager.denyRequests(context, groupId, Collections.singleton(recipient.getId()));
|
||||
callback.onComplete(null);
|
||||
} catch (GroupChangeException | IOException e) {
|
||||
Log.w(TAG, e);
|
||||
|
||||
@@ -9,6 +9,7 @@ import android.widget.Toast;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
@@ -16,6 +17,7 @@ import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.groups.ui.AdminActionsListener;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupMemberListView;
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupLinkUrlAndStatus;
|
||||
import org.thoughtcrime.securesms.recipients.ui.bottomsheet.RecipientBottomSheetDialogFragment;
|
||||
import org.thoughtcrime.securesms.util.BottomSheetUtil;
|
||||
|
||||
@@ -29,9 +31,9 @@ public class RequestingMembersFragment extends Fragment {
|
||||
private static final String GROUP_ID = "GROUP_ID";
|
||||
|
||||
private RequestingMemberInvitesViewModel viewModel;
|
||||
private GroupMemberListView requestingMembers;
|
||||
private View noRequestingMessage;
|
||||
private View requestingExplanation;
|
||||
private GroupMemberListView requestingMembers;
|
||||
private View noRequestingMessage;
|
||||
private View requestingExplanation;
|
||||
|
||||
public static RequestingMembersFragment newInstance(@NonNull GroupId.V2 groupId) {
|
||||
RequestingMembersFragment fragment = new RequestingMembersFragment();
|
||||
@@ -53,9 +55,10 @@ public class RequestingMembersFragment extends Fragment {
|
||||
|
||||
requestingMembers.initializeAdapter(getViewLifecycleOwner());
|
||||
|
||||
requestingMembers.setRecipientClickListener(recipient ->
|
||||
requestingMembers.setRecipientClickListener(recipient -> {
|
||||
RecipientBottomSheetDialogFragment.create(recipient.getId(), null)
|
||||
.show(requireActivity().getSupportFragmentManager(), BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG));
|
||||
.show(requireActivity().getSupportFragmentManager(), BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG);
|
||||
});
|
||||
|
||||
requestingMembers.setAdminActionsListener(new AdminActionsListener() {
|
||||
|
||||
@@ -71,31 +74,37 @@ public class RequestingMembersFragment extends Fragment {
|
||||
|
||||
@Override
|
||||
public void onApproveRequest(@NonNull GroupMemberEntry.RequestingMember requestingMember) {
|
||||
viewModel.approveRequestFor(requestingMember);
|
||||
//noinspection CodeBlock2Expr
|
||||
RequestConfirmationDialog.showApprove(requireContext(), requestingMember.getRequester(), () -> {
|
||||
viewModel.approveRequestFor(requestingMember);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDenyRequest(@NonNull GroupMemberEntry.RequestingMember requestingMember) {
|
||||
viewModel.denyRequestFor(requestingMember);
|
||||
GroupLinkUrlAndStatus linkStatus = viewModel.getInviteLink().getValue();
|
||||
boolean linkEnabled = linkStatus == null || linkStatus.isEnabled();
|
||||
|
||||
//noinspection CodeBlock2Expr
|
||||
RequestConfirmationDialog.showDeny(requireContext(), requestingMember.getRequester(), linkEnabled, () -> {
|
||||
viewModel.denyRequestFor(requestingMember);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
GroupId.V2 groupId = GroupId.parseOrThrow(Objects.requireNonNull(requireArguments().getString(GROUP_ID))).requireV2();
|
||||
|
||||
RequestingMemberInvitesViewModel.Factory factory = new RequestingMemberInvitesViewModel.Factory(requireContext(), groupId);
|
||||
|
||||
viewModel = ViewModelProviders.of(requireActivity(), factory).get(RequestingMemberInvitesViewModel.class);
|
||||
viewModel = new ViewModelProvider(this, factory).get(RequestingMemberInvitesViewModel.class);
|
||||
|
||||
viewModel.getRequesting().observe(getViewLifecycleOwner(), requesting -> {
|
||||
requestingMembers.setMembers(requesting);
|
||||
noRequestingMessage.setVisibility(requesting.isEmpty() ? View.VISIBLE: View.GONE);
|
||||
noRequestingMessage.setVisibility(requesting.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
requestingExplanation.setVisibility(requesting.isEmpty() ? View.GONE : View.VISIBLE);
|
||||
});
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ class ReviewCardRepository {
|
||||
|
||||
SignalExecutors.BOUNDED.execute(() -> {
|
||||
try {
|
||||
GroupManager.ejectFromGroup(context, groupId, reviewCard.getReviewRecipient());
|
||||
GroupManager.ejectAndBanFromGroup(context, groupId, reviewCard.getReviewRecipient());
|
||||
onRemoveFromGroupListener.onActionCompleted();
|
||||
} catch (GroupChangeException | IOException e) {
|
||||
onRemoveFromGroupListener.onActionFailed();
|
||||
|
||||
@@ -290,6 +290,10 @@ public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogF
|
||||
makeGroupAdminButton.setVisibility(adminStatus.isCanMakeAdmin() ? View.VISIBLE : View.GONE);
|
||||
removeAdminButton.setVisibility(adminStatus.isCanMakeNonAdmin() ? View.VISIBLE : View.GONE);
|
||||
removeFromGroupButton.setVisibility(adminStatus.isCanRemove() ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (adminStatus.isCanRemove()) {
|
||||
removeFromGroupButton.setOnClickListener(view -> viewModel.onRemoveFromGroupClicked(requireActivity(), adminStatus.isLinkActive(), this::dismiss));
|
||||
}
|
||||
});
|
||||
|
||||
viewModel.getIdentity().observe(getViewLifecycleOwner(), identityRecord -> {
|
||||
@@ -319,8 +323,6 @@ public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogF
|
||||
makeGroupAdminButton.setOnClickListener(view -> viewModel.onMakeGroupAdminClicked(requireActivity()));
|
||||
removeAdminButton.setOnClickListener(view -> viewModel.onRemoveGroupAdminClicked(requireActivity()));
|
||||
|
||||
removeFromGroupButton.setOnClickListener(view -> viewModel.onRemoveFromGroupClicked(requireActivity(), this::dismiss));
|
||||
|
||||
addToGroupButton.setOnClickListener(view -> {
|
||||
dismiss();
|
||||
viewModel.onAddToGroupButton(requireActivity());
|
||||
|
||||
@@ -77,7 +77,7 @@ final class RecipientDialogRepository {
|
||||
SimpleTask.run(SignalExecutors.UNBOUNDED,
|
||||
() -> {
|
||||
try {
|
||||
GroupManager.ejectFromGroup(context, Objects.requireNonNull(groupId).requireV2(), Recipient.resolved(recipientId));
|
||||
GroupManager.ejectAndBanFromGroup(context, Objects.requireNonNull(groupId).requireV2(), Recipient.resolved(recipientId));
|
||||
return true;
|
||||
} catch (GroupChangeException | IOException e) {
|
||||
Log.w(TAG, e);
|
||||
|
||||
@@ -15,6 +15,8 @@ import androidx.lifecycle.Transformations;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import org.signal.core.util.ThreadUtil;
|
||||
import org.thoughtcrime.securesms.BlockUnblockDialog;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
@@ -33,6 +35,7 @@ import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||
import org.thoughtcrime.securesms.util.CommunicationActions;
|
||||
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
|
||||
import org.thoughtcrime.securesms.verify.VerifyIdentityActivity;
|
||||
import org.whispersystems.libsignal.util.Pair;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -68,20 +71,22 @@ final class RecipientDialogViewModel extends ViewModel {
|
||||
if (recipientDialogRepository.getGroupId() != null && recipientDialogRepository.getGroupId().isV2() && !recipientIsSelf) {
|
||||
LiveGroup source = new LiveGroup(recipientDialogRepository.getGroupId());
|
||||
|
||||
LiveData<Boolean> localIsAdmin = source.isSelfAdmin();
|
||||
LiveData<Pair<Boolean, Boolean>> localStatus = LiveDataUtil.combineLatest(source.isSelfAdmin(), Transformations.map(source.getGroupLink(), s -> s == null || s.isEnabled()), Pair::new);
|
||||
LiveData<GroupDatabase.MemberLevel> recipientMemberLevel = Transformations.switchMap(recipient, source::getMemberLevel);
|
||||
|
||||
adminActionStatus = LiveDataUtil.combineLatest(localIsAdmin, recipientMemberLevel,
|
||||
(localAdmin, memberLevel) -> {
|
||||
boolean inGroup = memberLevel.isInGroup();
|
||||
boolean recipientAdmin = memberLevel == GroupDatabase.MemberLevel.ADMINISTRATOR;
|
||||
adminActionStatus = LiveDataUtil.combineLatest(localStatus, recipientMemberLevel, (statuses, memberLevel) -> {
|
||||
boolean localAdmin = statuses.first();
|
||||
boolean isLinkActive = statuses.second();
|
||||
boolean inGroup = memberLevel.isInGroup();
|
||||
boolean recipientAdmin = memberLevel == GroupDatabase.MemberLevel.ADMINISTRATOR;
|
||||
|
||||
return new AdminActionStatus(inGroup && localAdmin,
|
||||
inGroup && localAdmin && !recipientAdmin,
|
||||
inGroup && localAdmin && recipientAdmin);
|
||||
});
|
||||
return new AdminActionStatus(inGroup && localAdmin,
|
||||
inGroup && localAdmin && !recipientAdmin,
|
||||
inGroup && localAdmin && recipientAdmin,
|
||||
isLinkActive);
|
||||
});
|
||||
} else {
|
||||
adminActionStatus = new MutableLiveData<>(new AdminActionStatus(false, false, false));
|
||||
adminActionStatus = new MutableLiveData<>(new AdminActionStatus(false, false, false, false));
|
||||
}
|
||||
|
||||
boolean isSelf = recipientDialogRepository.getRecipientId().equals(Recipient.self().getId());
|
||||
@@ -164,7 +169,7 @@ final class RecipientDialogViewModel extends ViewModel {
|
||||
}
|
||||
|
||||
void onMakeGroupAdminClicked(@NonNull Activity activity) {
|
||||
new AlertDialog.Builder(activity)
|
||||
new MaterialAlertDialogBuilder(activity)
|
||||
.setMessage(context.getString(R.string.RecipientBottomSheet_s_will_be_able_to_edit_group, Objects.requireNonNull(recipient.getValue()).getDisplayName(context)))
|
||||
.setPositiveButton(R.string.RecipientBottomSheet_make_admin,
|
||||
(dialog, which) -> {
|
||||
@@ -182,7 +187,7 @@ final class RecipientDialogViewModel extends ViewModel {
|
||||
}
|
||||
|
||||
void onRemoveGroupAdminClicked(@NonNull Activity activity) {
|
||||
new AlertDialog.Builder(activity)
|
||||
new MaterialAlertDialogBuilder(activity)
|
||||
.setMessage(context.getString(R.string.RecipientBottomSheet_remove_s_as_group_admin, Objects.requireNonNull(recipient.getValue()).getDisplayName(context)))
|
||||
.setPositiveButton(R.string.RecipientBottomSheet_remove_as_admin,
|
||||
(dialog, which) -> {
|
||||
@@ -199,9 +204,11 @@ final class RecipientDialogViewModel extends ViewModel {
|
||||
.show();
|
||||
}
|
||||
|
||||
void onRemoveFromGroupClicked(@NonNull Activity activity, @NonNull Runnable onSuccess) {
|
||||
new AlertDialog.Builder(activity)
|
||||
.setMessage(context.getString(R.string.RecipientBottomSheet_remove_s_from_the_group, Objects.requireNonNull(recipient.getValue()).getDisplayName(context)))
|
||||
void onRemoveFromGroupClicked(@NonNull Activity activity, boolean isLinkActive, @NonNull Runnable onSuccess) {
|
||||
new MaterialAlertDialogBuilder(activity)
|
||||
.setMessage(context.getString(isLinkActive ? R.string.RecipientBottomSheet_remove_s_from_the_group_they_will_not_be_able_to_rejoin
|
||||
: R.string.RecipientBottomSheet_remove_s_from_the_group,
|
||||
Objects.requireNonNull(recipient.getValue()).getDisplayName(context)))
|
||||
.setPositiveButton(R.string.RecipientBottomSheet_remove,
|
||||
(dialog, which) -> {
|
||||
adminActionBusy.setValue(true);
|
||||
@@ -234,11 +241,13 @@ final class RecipientDialogViewModel extends ViewModel {
|
||||
private final boolean canRemove;
|
||||
private final boolean canMakeAdmin;
|
||||
private final boolean canMakeNonAdmin;
|
||||
private final boolean isLinkActive;
|
||||
|
||||
AdminActionStatus(boolean canRemove, boolean canMakeAdmin, boolean canMakeNonAdmin) {
|
||||
AdminActionStatus(boolean canRemove, boolean canMakeAdmin, boolean canMakeNonAdmin, boolean isLinkActive) {
|
||||
this.canRemove = canRemove;
|
||||
this.canMakeAdmin = canMakeAdmin;
|
||||
this.canMakeNonAdmin = canMakeNonAdmin;
|
||||
this.isLinkActive = isLinkActive;
|
||||
}
|
||||
|
||||
boolean isCanRemove() {
|
||||
@@ -252,6 +261,10 @@ final class RecipientDialogViewModel extends ViewModel {
|
||||
boolean isCanMakeNonAdmin() {
|
||||
return canMakeNonAdmin;
|
||||
}
|
||||
|
||||
boolean isLinkActive() {
|
||||
return isLinkActive;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Factory implements ViewModelProvider.Factory {
|
||||
|
||||
Reference in New Issue
Block a user