From 644af87782465e2ad8f8b3d43e1a62ae662ee592 Mon Sep 17 00:00:00 2001 From: Alan Evans Date: Wed, 15 Jul 2020 15:40:13 -0300 Subject: [PATCH] Groups V2 invite decline. --- .../conversation/ConversationActivity.java | 23 +++++- .../securesms/groups/GroupManager.java | 10 ++- .../securesms/groups/GroupManagerV2.java | 18 ++++- .../ui/managegroup/ManageGroupRepository.java | 12 ++++ .../ui/managegroup/ManageGroupViewModel.java | 9 ++- .../securesms/jobs/SendReadReceiptJob.java | 5 ++ .../MessageRequestRepository.java | 55 +++++++++++---- .../MessageRequestViewModel.java | 70 ++++++++++++------- .../MessageRequestsBottomView.java | 36 ++++++++-- .../securesms/recipients/RecipientUtil.java | 48 ++++++++----- .../bottomsheet/RecipientDialogViewModel.java | 2 +- .../ManageRecipientViewModel.java | 2 +- .../res/layout/message_request_bottom_bar.xml | 42 +++++++---- 13 files changed, 245 insertions(+), 87 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java index d0b6df505..a620460bc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -2847,25 +2847,44 @@ public class ConversationActivity extends PassphraseRequiredActivity @Override public void onMessageRequest(@NonNull MessageRequestViewModel viewModel) { - messageRequestBottomView.setAcceptOnClickListener(v -> viewModel.onAccept(this::showGroupChangeErrorToast)); + messageRequestBottomView.setAcceptOnClickListener(v -> viewModel.onAccept()); messageRequestBottomView.setDeleteOnClickListener(v -> onMessageRequestDeleteClicked(viewModel)); messageRequestBottomView.setBlockOnClickListener(v -> onMessageRequestBlockClicked(viewModel)); messageRequestBottomView.setUnblockOnClickListener(v -> onMessageRequestUnblockClicked(viewModel)); viewModel.getRecipient().observe(this, this::presentMessageRequestBottomViewTo); viewModel.getMessageRequestDisplayState().observe(this, this::presentMessageRequestDisplayState); + viewModel.getFailures().observe(this, this::showGroupChangeErrorToast); viewModel.getMessageRequestStatus().observe(this, status -> { switch (status) { + case IDLE: + hideMessageRequestBusy(); + break; + case ACCEPTING: + case BLOCKING: + case DELETING: + showMessageRequestBusy(); + break; case ACCEPTED: + hideMessageRequestBusy(); messageRequestBottomView.setVisibility(View.GONE); - return; + break; case DELETED: case BLOCKED: + hideMessageRequestBusy(); finish(); } }); } + private void showMessageRequestBusy() { + messageRequestBottomView.showBusy(); + } + + private void hideMessageRequestBusy() { + messageRequestBottomView.hideBusy(); + } + private void showGroupChangeErrorToast(@NonNull GroupChangeFailureReason e) { Toast.makeText(this, GroupErrors.getUserDisplayMessage(e), Toast.LENGTH_LONG).show(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManager.java b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManager.java index 7cecd86e2..f4e2fae73 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManager.java @@ -127,11 +127,15 @@ public final class GroupManager { } @WorkerThread - public static boolean silentLeaveGroup(@NonNull Context context, @NonNull GroupId.Push groupId) { + public static void leaveGroupFromBlockOrMessageRequest(@NonNull Context context, @NonNull GroupId.Push groupId) + throws IOException, GroupChangeBusyException, GroupChangeFailedException + { if (groupId.isV2()) { - throw new AssertionError("NYI"); // TODO [Alan] GV2 support silent leave for block and leave operations on GV2 + leaveGroup(context, groupId.requireV2()); } else { - return GroupManagerV1.silentLeaveGroup(context, groupId.requireV1()); + if (!GroupManagerV1.silentLeaveGroup(context, groupId.requireV1())) { + throw new GroupChangeFailedException(); + } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java index fffd2926e..38cc0f897 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java @@ -14,6 +14,8 @@ import org.signal.storageservice.protos.groups.Member; import org.signal.storageservice.protos.groups.local.DecryptedGroup; import org.signal.storageservice.protos.groups.local.DecryptedGroupChange; import org.signal.storageservice.protos.groups.local.DecryptedMember; +import org.signal.storageservice.protos.groups.local.DecryptedPendingMember; +import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.VerificationFailedException; import org.signal.zkgroup.groups.GroupMasterKey; import org.signal.zkgroup.groups.GroupSecretParams; @@ -54,6 +56,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.UUID; @@ -268,7 +271,20 @@ final class GroupManagerV2 { @NonNull GroupManager.GroupActionResult leaveGroup() throws GroupChangeFailedException, GroupInsufficientRightsException, IOException, GroupNotAMemberException { - return ejectMember(Recipient.self().getId()); + Recipient self = Recipient.self(); + GroupDatabase.GroupRecord groupRecord = groupDatabase.getGroup(groupId).get(); + List pendingMembersList = groupRecord.requireV2GroupProperties().getDecryptedGroup().getPendingMembersList(); + Optional selfPendingMember = DecryptedGroupUtil.findPendingByUuid(pendingMembersList, selfUuid); + + if (selfPendingMember.isPresent()) { + try { + return cancelInvites(Collections.singleton(new UuidCiphertext(selfPendingMember.get().getUuidCipherText().toByteArray()))); + } catch (InvalidInputException e) { + throw new AssertionError(e); + } + } else { + return ejectMember(self.getId()); + } } @WorkerThread diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupRepository.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupRepository.java index b66baac05..0d57e778a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupRepository.java @@ -28,6 +28,7 @@ import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; +import org.thoughtcrime.securesms.recipients.RecipientUtil; import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.concurrent.SignalExecutors; import org.thoughtcrime.securesms.util.concurrent.SimpleTask; @@ -160,6 +161,17 @@ final class ManageGroupRepository { }); } + void blockAndLeaveGroup(@NonNull GroupChangeErrorCallback error) { + SignalExecutors.UNBOUNDED.execute(() -> { + try { + RecipientUtil.block(context, Recipient.externalGroup(context, groupId)); + } catch (GroupChangeFailedException | GroupChangeBusyException | IOException e) { + Log.w(TAG, e); + error.onError(GroupChangeFailureReason.OTHER); + } + }); + } + static final class GroupStateResult { private final long threadId; diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupViewModel.java index af4725026..aeb236274 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupViewModel.java @@ -25,6 +25,8 @@ import org.thoughtcrime.securesms.database.MediaDatabase; import org.thoughtcrime.securesms.database.loaders.MediaLoader; import org.thoughtcrime.securesms.database.loaders.ThreadMediaLoader; import org.thoughtcrime.securesms.groups.GroupAccessControl; +import org.thoughtcrime.securesms.groups.GroupChangeBusyException; +import org.thoughtcrime.securesms.groups.GroupChangeFailedException; import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.groups.LiveGroup; import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason; @@ -41,6 +43,7 @@ import org.thoughtcrime.securesms.util.SingleLiveEvent; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.livedata.LiveDataUtil; +import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -205,8 +208,10 @@ public class ManageGroupViewModel extends ViewModel { } void blockAndLeave(@NonNull FragmentActivity activity) { - manageGroupRepository.getRecipient(recipient -> BlockUnblockDialog.showBlockFor(activity, activity.getLifecycle(), recipient, - () -> RecipientUtil.block(context, recipient))); + manageGroupRepository.getRecipient(recipient -> BlockUnblockDialog.showBlockFor(activity, + activity.getLifecycle(), + recipient, + () -> manageGroupRepository.blockAndLeaveGroup(this::showErrorToast))); } void unblock(@NonNull FragmentActivity activity) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java index db2c6ad80..4340df17b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java @@ -103,6 +103,11 @@ public class SendReadReceiptJob extends BaseJob { return; } + if (recipient.isGroup()) { + Log.w(TAG, "Refusing to send receipts to group"); + return; + } + SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender(); SignalServiceAddress remoteAddress = RecipientUtil.toSignalServiceAddress(context, recipient); SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, messageIds, timestamp); diff --git a/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestRepository.java b/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestRepository.java index 98c909472..0f01c5d98 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestRepository.java @@ -28,7 +28,6 @@ import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.recipients.RecipientUtil; import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.concurrent.SignalExecutors; import org.whispersystems.libsignal.util.guava.Optional; @@ -57,8 +56,8 @@ final class MessageRequestRepository { void getMemberCount(@NonNull RecipientId recipientId, @NonNull Consumer onMemberCountLoaded) { executor.execute(() -> { - GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); - Optional groupRecord = groupDatabase.getGroup(recipientId); + GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); + Optional groupRecord = groupDatabase.getGroup(recipientId); onMemberCountLoaded.accept(groupRecord.transform(record -> { if (record.isV2Group()) { DecryptedGroup decryptedGroup = record.requireV2GroupProperties().getDecryptedGroup(); @@ -90,9 +89,8 @@ final class MessageRequestRepository { void acceptMessageRequest(@NonNull LiveRecipient liveRecipient, long threadId, @NonNull Runnable onMessageRequestAccepted, - @NonNull GroupChangeErrorCallback mainThreadError) + @NonNull GroupChangeErrorCallback error) { - GroupChangeErrorCallback error = e -> Util.runOnMain(() -> mainThreadError.onError(e)); executor.execute(()-> { if (liveRecipient.get().isPushV2Group()) { try { @@ -130,27 +128,47 @@ final class MessageRequestRepository { }); } - void deleteMessageRequest(@NonNull LiveRecipient recipient, long threadId, @NonNull Runnable onMessageRequestDeleted) { + void deleteMessageRequest(@NonNull LiveRecipient recipient, + long threadId, + @NonNull Runnable onMessageRequestDeleted, + @NonNull GroupChangeErrorCallback error) + { executor.execute(() -> { - ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context); - threadDatabase.deleteConversation(threadId); + Recipient resolved = recipient.resolve(); - if (recipient.resolve().isGroup()) { - RecipientUtil.leaveGroup(context, recipient.get()); + if (resolved.isGroup() && resolved.requireGroupId().isPush()) { + try { + GroupManager.leaveGroupFromBlockOrMessageRequest(context, resolved.requireGroupId().requirePush()); + } catch (GroupChangeBusyException | GroupChangeFailedException | IOException e) { + Log.w(TAG, e); + error.onError(GroupChangeFailureReason.OTHER); + return; + } } if (TextSecurePreferences.isMultiDevice(context)) { ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forDelete(recipient.getId())); } + ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context); + threadDatabase.deleteConversation(threadId); + onMessageRequestDeleted.run(); }); } - void blockMessageRequest(@NonNull LiveRecipient liveRecipient, @NonNull Runnable onMessageRequestBlocked) { + void blockMessageRequest(@NonNull LiveRecipient liveRecipient, + @NonNull Runnable onMessageRequestBlocked, + @NonNull GroupChangeErrorCallback error) + { executor.execute(() -> { Recipient recipient = liveRecipient.resolve(); - RecipientUtil.block(context, recipient); + try { + RecipientUtil.block(context, recipient); + } catch (GroupChangeBusyException | GroupChangeFailedException | IOException e) { + error.onError(GroupChangeFailureReason.OTHER); + return; + } liveRecipient.refresh(); if (TextSecurePreferences.isMultiDevice(context)) { @@ -161,10 +179,19 @@ final class MessageRequestRepository { }); } - void blockAndDeleteMessageRequest(@NonNull LiveRecipient liveRecipient, long threadId, @NonNull Runnable onMessageRequestBlocked) { + void blockAndDeleteMessageRequest(@NonNull LiveRecipient liveRecipient, + long threadId, + @NonNull Runnable onMessageRequestBlocked, + @NonNull GroupChangeErrorCallback error) + { executor.execute(() -> { Recipient recipient = liveRecipient.resolve(); - RecipientUtil.block(context, recipient); + try{ + RecipientUtil.block(context, recipient); + } catch (GroupChangeBusyException | GroupChangeFailedException | IOException e) { + error.onError(GroupChangeFailureReason.OTHER); + return; + } liveRecipient.refresh(); DatabaseFactory.getThreadDatabase(context).deleteConversation(threadId); diff --git a/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestViewModel.java index dec275ce1..88e472017 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestViewModel.java @@ -11,12 +11,11 @@ import androidx.lifecycle.Transformations; import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModelProvider; -import org.thoughtcrime.securesms.groups.ui.GroupChangeErrorCallback; +import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason; import org.thoughtcrime.securesms.recipients.LiveRecipient; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientForeverObserver; import org.thoughtcrime.securesms.recipients.RecipientId; -import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.SingleLiveEvent; import org.thoughtcrime.securesms.util.concurrent.SignalExecutors; import org.thoughtcrime.securesms.util.livedata.LiveDataTriple; @@ -26,13 +25,14 @@ import java.util.List; public class MessageRequestViewModel extends ViewModel { - private final SingleLiveEvent status = new SingleLiveEvent<>(); - private final MutableLiveData recipient = new MutableLiveData<>(); - private final MutableLiveData> groups = new MutableLiveData<>(Collections.emptyList()); - private final MutableLiveData memberCount = new MutableLiveData<>(GroupMemberCount.ZERO); - private final MutableLiveData displayState = new MutableLiveData<>(); - private final LiveData recipientInfo = Transformations.map(new LiveDataTriple<>(recipient, memberCount, groups), - triple -> new RecipientInfo(triple.first(), triple.second(), triple.third())); + private final SingleLiveEvent status = new SingleLiveEvent<>(); + private final SingleLiveEvent failures = new SingleLiveEvent<>(); + private final MutableLiveData recipient = new MutableLiveData<>(); + private final MutableLiveData> groups = new MutableLiveData<>(Collections.emptyList()); + private final MutableLiveData memberCount = new MutableLiveData<>(GroupMemberCount.ZERO); + private final MutableLiveData displayState = new MutableLiveData<>(); + private final LiveData recipientInfo = Transformations.map(new LiveDataTriple<>(recipient, memberCount, groups), + triple -> new RecipientInfo(triple.first(), triple.second(), triple.third())); private final MessageRequestRepository repository; @@ -85,44 +85,58 @@ public class MessageRequestViewModel extends ViewModel { return status; } + public LiveData getFailures() { + return failures; + } + public boolean shouldShowMessageRequest() { return displayState.getValue() == DisplayState.DISPLAY_MESSAGE_REQUEST; } @MainThread - public void onAccept(@NonNull GroupChangeErrorCallback error) { - repository.acceptMessageRequest(liveRecipient, threadId, () -> { - status.postValue(Status.ACCEPTED); - }, - error); + public void onAccept() { + status.setValue(Status.ACCEPTING); + repository.acceptMessageRequest(liveRecipient, + threadId, + () -> status.postValue(Status.ACCEPTED), + this::onGroupChangeError); } @MainThread public void onDelete() { - repository.deleteMessageRequest(liveRecipient, threadId, () -> { - status.postValue(Status.DELETED); - }); + status.setValue(Status.DELETING); + repository.deleteMessageRequest(liveRecipient, + threadId, + () -> status.postValue(Status.DELETED), + this::onGroupChangeError); } @MainThread public void onBlock() { - repository.blockMessageRequest(liveRecipient, () -> { - status.postValue(Status.BLOCKED); - }); + status.setValue(Status.BLOCKING); + repository.blockMessageRequest(liveRecipient, + () -> status.postValue(Status.BLOCKED), + this::onGroupChangeError); } @MainThread public void onUnblock() { - repository.unblockAndAccept(liveRecipient, threadId, () -> { - status.postValue(Status.ACCEPTED); - }); + repository.unblockAndAccept(liveRecipient, + threadId, + () -> status.postValue(Status.ACCEPTED)); } @MainThread public void onBlockAndDelete() { - repository.blockAndDeleteMessageRequest(liveRecipient, threadId, () -> { - status.postValue(Status.BLOCKED); - }); + repository.blockAndDeleteMessageRequest(liveRecipient, + threadId, + () -> status.postValue(Status.BLOCKED), + this::onGroupChangeError); + } + + private void onGroupChangeError(@NonNull GroupChangeFailureReason error) { + status.postValue(Status.IDLE); + failures.postValue(error); } private void loadRecipient() { @@ -191,8 +205,12 @@ public class MessageRequestViewModel extends ViewModel { } public enum Status { + IDLE, + BLOCKING, BLOCKED, + DELETING, DELETED, + ACCEPTING, ACCEPTED } diff --git a/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsBottomView.java b/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsBottomView.java index f986db7d1..ebe651620 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsBottomView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messagerequests/MessageRequestsBottomView.java @@ -12,19 +12,24 @@ import androidx.core.text.HtmlCompat; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.util.Debouncer; import org.thoughtcrime.securesms.util.HtmlUtil; public class MessageRequestsBottomView extends ConstraintLayout { + private final Debouncer showProgressDebouncer = new Debouncer(250); + private TextView question; private View accept; private View block; private View delete; private View bigDelete; private View bigUnblock; + private View busyIndicator; private Group normalButtons; private Group blockedButtons; + private Group activeGroup; public MessageRequestsBottomView(Context context) { super(context); @@ -52,6 +57,7 @@ public class MessageRequestsBottomView extends ConstraintLayout { bigUnblock = findViewById(R.id.message_request_big_unblock); normalButtons = findViewById(R.id.message_request_normal_buttons); blockedButtons = findViewById(R.id.message_request_blocked_buttons); + busyIndicator = findViewById(R.id.message_request_busy_indicator); } public void setRecipient(@NonNull Recipient recipient) { @@ -62,8 +68,7 @@ public class MessageRequestsBottomView extends ConstraintLayout { String name = recipient.getProfileName().isEmpty() ? recipient.getDisplayName(getContext()) : recipient.getProfileName().getGivenName(); question.setText(HtmlCompat.fromHtml(getContext().getString(R.string.MessageRequestBottomView_do_you_want_to_let_s_message_you_wont_receive_any_messages_until_you_unblock_them, HtmlUtil.bold(name)), 0)); } - normalButtons.setVisibility(GONE); - blockedButtons.setVisibility(VISIBLE); + setActiveInactiveGroups(blockedButtons, normalButtons); } else { if (recipient.isGroup()) { if (recipient.isPushV2Group()) { @@ -75,8 +80,31 @@ public class MessageRequestsBottomView extends ConstraintLayout { String name = recipient.getProfileName().isEmpty() ? recipient.getDisplayName(getContext()) : recipient.getProfileName().getGivenName(); question.setText(HtmlCompat.fromHtml(getContext().getString(R.string.MessageRequestBottomView_do_you_want_to_let_s_message_you_they_wont_know_youve_seen_their_messages_until_you_accept, HtmlUtil.bold(name)), 0)); } - normalButtons.setVisibility(VISIBLE); - blockedButtons.setVisibility(GONE); + setActiveInactiveGroups(normalButtons, blockedButtons); + } + } + + private void setActiveInactiveGroups(@NonNull Group activeGroup, @NonNull Group inActiveGroup) { + int initialVisibility = this.activeGroup != null ? this.activeGroup.getVisibility() : VISIBLE; + + this.activeGroup = activeGroup; + + inActiveGroup.setVisibility(GONE); + activeGroup.setVisibility(initialVisibility); + } + + public void showBusy() { + showProgressDebouncer.publish(() -> busyIndicator.setVisibility(VISIBLE)); + if (activeGroup != null) { + activeGroup.setVisibility(INVISIBLE); + } + } + + public void hideBusy() { + showProgressDebouncer.clear(); + busyIndicator.setVisibility(GONE); + if (activeGroup != null) { + activeGroup.setVisibility(VISIBLE); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientUtil.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientUtil.java index cf2ea018b..7fafc1b8b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientUtil.java @@ -1,7 +1,6 @@ package org.thoughtcrime.securesms.recipients; import android.content.Context; -import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -9,12 +8,13 @@ import androidx.annotation.WorkerThread; import com.annimon.stream.Stream; -import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState; import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; +import org.thoughtcrime.securesms.groups.GroupChangeBusyException; +import org.thoughtcrime.securesms.groups.GroupChangeFailedException; import org.thoughtcrime.securesms.groups.GroupManager; import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob; import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob; @@ -66,8 +66,32 @@ public class RecipientUtil { return resolved.isPushGroup() || resolved.hasServiceIdentifier(); } + /** + * You can call this for non-groups and not have to handle any network errors. + */ @WorkerThread - public static void block(@NonNull Context context, @NonNull Recipient recipient) { + public static void blockNonGroup(@NonNull Context context, @NonNull Recipient recipient) { + if (recipient.isGroup()) { + throw new AssertionError(); + } + + try { + block(context, recipient); + } catch (GroupChangeBusyException | IOException | GroupChangeFailedException e) { + throw new AssertionError(e); + } + } + + /** + * You can call this for any type of recipient but must handle network errors that can occur from + * GV2. + *

+ * GV2 operations can also take longer due to the network. + */ + @WorkerThread + public static void block(@NonNull Context context, @NonNull Recipient recipient) + throws GroupChangeBusyException, IOException, GroupChangeFailedException + { if (!isBlockable(recipient)) { throw new AssertionError("Recipient is not blockable!"); } @@ -76,8 +100,8 @@ public class RecipientUtil { DatabaseFactory.getRecipientDatabase(context).setBlocked(resolved.getId(), true); - if (resolved.isGroup()) { - leaveGroup(context, recipient); + if (resolved.isGroup() && recipient.getGroupId().get().isPush()) { + GroupManager.leaveGroupFromBlockOrMessageRequest(context, recipient.getGroupId().get().requirePush()); } if (resolved.isSystemContact() || resolved.isProfileSharing() || isProfileSharedViaGroup(context,resolved)) { @@ -101,20 +125,6 @@ public class RecipientUtil { ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forAccept(recipient.getId())); } - @WorkerThread - public static void leaveGroup(@NonNull Context context, @NonNull Recipient recipient) { - Recipient resolved = recipient.resolve(); - - if (!resolved.isGroup()) { - throw new AssertionError("Not a group!"); - } - - if (!GroupManager.silentLeaveGroup(context, resolved.requireGroupId().requirePush())) { - Log.w(TAG, "Failed to leave group."); - Toast.makeText(context, R.string.RecipientPreferenceActivity_error_leaving_group, Toast.LENGTH_LONG).show(); - } - } - /** * If true, the new message request UI does not need to be shown, and it's safe to send read * receipts. diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogViewModel.java index 66a1e989e..b3783a6e4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/bottomsheet/RecipientDialogViewModel.java @@ -120,7 +120,7 @@ final class RecipientDialogViewModel extends ViewModel { } void onBlockClicked(@NonNull FragmentActivity activity) { - recipientDialogRepository.getRecipient(recipient -> BlockUnblockDialog.showBlockFor(activity, activity.getLifecycle(), recipient, () -> RecipientUtil.block(context, recipient))); + recipientDialogRepository.getRecipient(recipient -> BlockUnblockDialog.showBlockFor(activity, activity.getLifecycle(), recipient, () -> RecipientUtil.blockNonGroup(context, recipient))); } void onUnblockClicked(@NonNull FragmentActivity activity) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/managerecipient/ManageRecipientViewModel.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/managerecipient/ManageRecipientViewModel.java index c346bd4cc..07cc4470d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/managerecipient/ManageRecipientViewModel.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/ui/managerecipient/ManageRecipientViewModel.java @@ -210,7 +210,7 @@ public final class ManageRecipientViewModel extends ViewModel { } void onBlockClicked(@NonNull FragmentActivity activity) { - withRecipient(recipient -> BlockUnblockDialog.showBlockFor(activity, activity.getLifecycle(), recipient, () -> RecipientUtil.block(context, recipient))); + withRecipient(recipient -> BlockUnblockDialog.showBlockFor(activity, activity.getLifecycle(), recipient, () -> RecipientUtil.blockNonGroup(context, recipient))); } void onUnblockClicked(@NonNull FragmentActivity activity) { diff --git a/app/src/main/res/layout/message_request_bottom_bar.xml b/app/src/main/res/layout/message_request_bottom_bar.xml index 8fe4e4ec0..247f90985 100644 --- a/app/src/main/res/layout/message_request_bottom_bar.xml +++ b/app/src/main/res/layout/message_request_bottom_bar.xml @@ -11,8 +11,8 @@ android:layout_marginStart="16dp" android:layout_marginEnd="16dp" android:layout_marginBottom="11dp" - android:textAppearance="@style/Signal.Text.MessageRequest.Description" android:paddingTop="16dp" + android:textAppearance="@style/Signal.Text.MessageRequest.Description" app:layout_constraintBottom_toTopOf="@id/message_request_button_barrier" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" @@ -20,12 +20,12 @@