Groups V2 invite decline.
parent
1ce36c1069
commit
644af87782
|
@ -2847,25 +2847,44 @@ public class ConversationActivity extends PassphraseRequiredActivity
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessageRequest(@NonNull MessageRequestViewModel viewModel) {
|
public void onMessageRequest(@NonNull MessageRequestViewModel viewModel) {
|
||||||
messageRequestBottomView.setAcceptOnClickListener(v -> viewModel.onAccept(this::showGroupChangeErrorToast));
|
messageRequestBottomView.setAcceptOnClickListener(v -> viewModel.onAccept());
|
||||||
messageRequestBottomView.setDeleteOnClickListener(v -> onMessageRequestDeleteClicked(viewModel));
|
messageRequestBottomView.setDeleteOnClickListener(v -> onMessageRequestDeleteClicked(viewModel));
|
||||||
messageRequestBottomView.setBlockOnClickListener(v -> onMessageRequestBlockClicked(viewModel));
|
messageRequestBottomView.setBlockOnClickListener(v -> onMessageRequestBlockClicked(viewModel));
|
||||||
messageRequestBottomView.setUnblockOnClickListener(v -> onMessageRequestUnblockClicked(viewModel));
|
messageRequestBottomView.setUnblockOnClickListener(v -> onMessageRequestUnblockClicked(viewModel));
|
||||||
|
|
||||||
viewModel.getRecipient().observe(this, this::presentMessageRequestBottomViewTo);
|
viewModel.getRecipient().observe(this, this::presentMessageRequestBottomViewTo);
|
||||||
viewModel.getMessageRequestDisplayState().observe(this, this::presentMessageRequestDisplayState);
|
viewModel.getMessageRequestDisplayState().observe(this, this::presentMessageRequestDisplayState);
|
||||||
|
viewModel.getFailures().observe(this, this::showGroupChangeErrorToast);
|
||||||
viewModel.getMessageRequestStatus().observe(this, status -> {
|
viewModel.getMessageRequestStatus().observe(this, status -> {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
|
case IDLE:
|
||||||
|
hideMessageRequestBusy();
|
||||||
|
break;
|
||||||
|
case ACCEPTING:
|
||||||
|
case BLOCKING:
|
||||||
|
case DELETING:
|
||||||
|
showMessageRequestBusy();
|
||||||
|
break;
|
||||||
case ACCEPTED:
|
case ACCEPTED:
|
||||||
|
hideMessageRequestBusy();
|
||||||
messageRequestBottomView.setVisibility(View.GONE);
|
messageRequestBottomView.setVisibility(View.GONE);
|
||||||
return;
|
break;
|
||||||
case DELETED:
|
case DELETED:
|
||||||
case BLOCKED:
|
case BLOCKED:
|
||||||
|
hideMessageRequestBusy();
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showMessageRequestBusy() {
|
||||||
|
messageRequestBottomView.showBusy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideMessageRequestBusy() {
|
||||||
|
messageRequestBottomView.hideBusy();
|
||||||
|
}
|
||||||
|
|
||||||
private void showGroupChangeErrorToast(@NonNull GroupChangeFailureReason e) {
|
private void showGroupChangeErrorToast(@NonNull GroupChangeFailureReason e) {
|
||||||
Toast.makeText(this, GroupErrors.getUserDisplayMessage(e), Toast.LENGTH_LONG).show();
|
Toast.makeText(this, GroupErrors.getUserDisplayMessage(e), Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,11 +127,15 @@ public final class GroupManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@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()) {
|
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 {
|
} else {
|
||||||
return GroupManagerV1.silentLeaveGroup(context, groupId.requireV1());
|
if (!GroupManagerV1.silentLeaveGroup(context, groupId.requireV1())) {
|
||||||
|
throw new GroupChangeFailedException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.DecryptedGroup;
|
||||||
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
|
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
|
||||||
import org.signal.storageservice.protos.groups.local.DecryptedMember;
|
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.VerificationFailedException;
|
||||||
import org.signal.zkgroup.groups.GroupMasterKey;
|
import org.signal.zkgroup.groups.GroupMasterKey;
|
||||||
import org.signal.zkgroup.groups.GroupSecretParams;
|
import org.signal.zkgroup.groups.GroupSecretParams;
|
||||||
|
@ -54,6 +56,7 @@ import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@ -268,7 +271,20 @@ final class GroupManagerV2 {
|
||||||
@NonNull GroupManager.GroupActionResult leaveGroup()
|
@NonNull GroupManager.GroupActionResult leaveGroup()
|
||||||
throws GroupChangeFailedException, GroupInsufficientRightsException, IOException, GroupNotAMemberException
|
throws GroupChangeFailedException, GroupInsufficientRightsException, IOException, GroupNotAMemberException
|
||||||
{
|
{
|
||||||
return ejectMember(Recipient.self().getId());
|
Recipient self = Recipient.self();
|
||||||
|
GroupDatabase.GroupRecord groupRecord = groupDatabase.getGroup(groupId).get();
|
||||||
|
List<DecryptedPendingMember> pendingMembersList = groupRecord.requireV2GroupProperties().getDecryptedGroup().getPendingMembersList();
|
||||||
|
Optional<DecryptedPendingMember> 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
|
@WorkerThread
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||||
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
||||||
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
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 {
|
static final class GroupStateResult {
|
||||||
|
|
||||||
private final long threadId;
|
private final long threadId;
|
||||||
|
|
|
@ -25,6 +25,8 @@ import org.thoughtcrime.securesms.database.MediaDatabase;
|
||||||
import org.thoughtcrime.securesms.database.loaders.MediaLoader;
|
import org.thoughtcrime.securesms.database.loaders.MediaLoader;
|
||||||
import org.thoughtcrime.securesms.database.loaders.ThreadMediaLoader;
|
import org.thoughtcrime.securesms.database.loaders.ThreadMediaLoader;
|
||||||
import org.thoughtcrime.securesms.groups.GroupAccessControl;
|
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.GroupId;
|
||||||
import org.thoughtcrime.securesms.groups.LiveGroup;
|
import org.thoughtcrime.securesms.groups.LiveGroup;
|
||||||
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
|
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.Util;
|
||||||
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
|
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -205,8 +208,10 @@ public class ManageGroupViewModel extends ViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
void blockAndLeave(@NonNull FragmentActivity activity) {
|
void blockAndLeave(@NonNull FragmentActivity activity) {
|
||||||
manageGroupRepository.getRecipient(recipient -> BlockUnblockDialog.showBlockFor(activity, activity.getLifecycle(), recipient,
|
manageGroupRepository.getRecipient(recipient -> BlockUnblockDialog.showBlockFor(activity,
|
||||||
() -> RecipientUtil.block(context, recipient)));
|
activity.getLifecycle(),
|
||||||
|
recipient,
|
||||||
|
() -> manageGroupRepository.blockAndLeaveGroup(this::showErrorToast)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void unblock(@NonNull FragmentActivity activity) {
|
void unblock(@NonNull FragmentActivity activity) {
|
||||||
|
|
|
@ -103,6 +103,11 @@ public class SendReadReceiptJob extends BaseJob {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (recipient.isGroup()) {
|
||||||
|
Log.w(TAG, "Refusing to send receipts to group");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
|
||||||
SignalServiceAddress remoteAddress = RecipientUtil.toSignalServiceAddress(context, recipient);
|
SignalServiceAddress remoteAddress = RecipientUtil.toSignalServiceAddress(context, recipient);
|
||||||
SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, messageIds, timestamp);
|
SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, messageIds, timestamp);
|
||||||
|
|
|
@ -28,7 +28,6 @@ import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
|
||||||
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
|
||||||
|
@ -57,8 +56,8 @@ final class MessageRequestRepository {
|
||||||
|
|
||||||
void getMemberCount(@NonNull RecipientId recipientId, @NonNull Consumer<GroupMemberCount> onMemberCountLoaded) {
|
void getMemberCount(@NonNull RecipientId recipientId, @NonNull Consumer<GroupMemberCount> onMemberCountLoaded) {
|
||||||
executor.execute(() -> {
|
executor.execute(() -> {
|
||||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||||
Optional<GroupDatabase.GroupRecord> groupRecord = groupDatabase.getGroup(recipientId);
|
Optional<GroupDatabase.GroupRecord> groupRecord = groupDatabase.getGroup(recipientId);
|
||||||
onMemberCountLoaded.accept(groupRecord.transform(record -> {
|
onMemberCountLoaded.accept(groupRecord.transform(record -> {
|
||||||
if (record.isV2Group()) {
|
if (record.isV2Group()) {
|
||||||
DecryptedGroup decryptedGroup = record.requireV2GroupProperties().getDecryptedGroup();
|
DecryptedGroup decryptedGroup = record.requireV2GroupProperties().getDecryptedGroup();
|
||||||
|
@ -90,9 +89,8 @@ final class MessageRequestRepository {
|
||||||
void acceptMessageRequest(@NonNull LiveRecipient liveRecipient,
|
void acceptMessageRequest(@NonNull LiveRecipient liveRecipient,
|
||||||
long threadId,
|
long threadId,
|
||||||
@NonNull Runnable onMessageRequestAccepted,
|
@NonNull Runnable onMessageRequestAccepted,
|
||||||
@NonNull GroupChangeErrorCallback mainThreadError)
|
@NonNull GroupChangeErrorCallback error)
|
||||||
{
|
{
|
||||||
GroupChangeErrorCallback error = e -> Util.runOnMain(() -> mainThreadError.onError(e));
|
|
||||||
executor.execute(()-> {
|
executor.execute(()-> {
|
||||||
if (liveRecipient.get().isPushV2Group()) {
|
if (liveRecipient.get().isPushV2Group()) {
|
||||||
try {
|
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(() -> {
|
executor.execute(() -> {
|
||||||
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
Recipient resolved = recipient.resolve();
|
||||||
threadDatabase.deleteConversation(threadId);
|
|
||||||
|
|
||||||
if (recipient.resolve().isGroup()) {
|
if (resolved.isGroup() && resolved.requireGroupId().isPush()) {
|
||||||
RecipientUtil.leaveGroup(context, recipient.get());
|
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)) {
|
if (TextSecurePreferences.isMultiDevice(context)) {
|
||||||
ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forDelete(recipient.getId()));
|
ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forDelete(recipient.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||||
|
threadDatabase.deleteConversation(threadId);
|
||||||
|
|
||||||
onMessageRequestDeleted.run();
|
onMessageRequestDeleted.run();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void blockMessageRequest(@NonNull LiveRecipient liveRecipient, @NonNull Runnable onMessageRequestBlocked) {
|
void blockMessageRequest(@NonNull LiveRecipient liveRecipient,
|
||||||
|
@NonNull Runnable onMessageRequestBlocked,
|
||||||
|
@NonNull GroupChangeErrorCallback error)
|
||||||
|
{
|
||||||
executor.execute(() -> {
|
executor.execute(() -> {
|
||||||
Recipient recipient = liveRecipient.resolve();
|
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();
|
liveRecipient.refresh();
|
||||||
|
|
||||||
if (TextSecurePreferences.isMultiDevice(context)) {
|
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(() -> {
|
executor.execute(() -> {
|
||||||
Recipient recipient = liveRecipient.resolve();
|
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();
|
liveRecipient.refresh();
|
||||||
|
|
||||||
DatabaseFactory.getThreadDatabase(context).deleteConversation(threadId);
|
DatabaseFactory.getThreadDatabase(context).deleteConversation(threadId);
|
||||||
|
|
|
@ -11,12 +11,11 @@ import androidx.lifecycle.Transformations;
|
||||||
import androidx.lifecycle.ViewModel;
|
import androidx.lifecycle.ViewModel;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
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.LiveRecipient;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
|
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
|
||||||
import org.thoughtcrime.securesms.util.SingleLiveEvent;
|
import org.thoughtcrime.securesms.util.SingleLiveEvent;
|
||||||
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
||||||
import org.thoughtcrime.securesms.util.livedata.LiveDataTriple;
|
import org.thoughtcrime.securesms.util.livedata.LiveDataTriple;
|
||||||
|
@ -26,13 +25,14 @@ import java.util.List;
|
||||||
|
|
||||||
public class MessageRequestViewModel extends ViewModel {
|
public class MessageRequestViewModel extends ViewModel {
|
||||||
|
|
||||||
private final SingleLiveEvent<Status> status = new SingleLiveEvent<>();
|
private final SingleLiveEvent<Status> status = new SingleLiveEvent<>();
|
||||||
private final MutableLiveData<Recipient> recipient = new MutableLiveData<>();
|
private final SingleLiveEvent<GroupChangeFailureReason> failures = new SingleLiveEvent<>();
|
||||||
private final MutableLiveData<List<String>> groups = new MutableLiveData<>(Collections.emptyList());
|
private final MutableLiveData<Recipient> recipient = new MutableLiveData<>();
|
||||||
private final MutableLiveData<GroupMemberCount> memberCount = new MutableLiveData<>(GroupMemberCount.ZERO);
|
private final MutableLiveData<List<String>> groups = new MutableLiveData<>(Collections.emptyList());
|
||||||
private final MutableLiveData<DisplayState> displayState = new MutableLiveData<>();
|
private final MutableLiveData<GroupMemberCount> memberCount = new MutableLiveData<>(GroupMemberCount.ZERO);
|
||||||
private final LiveData<RecipientInfo> recipientInfo = Transformations.map(new LiveDataTriple<>(recipient, memberCount, groups),
|
private final MutableLiveData<DisplayState> displayState = new MutableLiveData<>();
|
||||||
triple -> new RecipientInfo(triple.first(), triple.second(), triple.third()));
|
private final LiveData<RecipientInfo> recipientInfo = Transformations.map(new LiveDataTriple<>(recipient, memberCount, groups),
|
||||||
|
triple -> new RecipientInfo(triple.first(), triple.second(), triple.third()));
|
||||||
|
|
||||||
private final MessageRequestRepository repository;
|
private final MessageRequestRepository repository;
|
||||||
|
|
||||||
|
@ -85,44 +85,58 @@ public class MessageRequestViewModel extends ViewModel {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LiveData<GroupChangeFailureReason> getFailures() {
|
||||||
|
return failures;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean shouldShowMessageRequest() {
|
public boolean shouldShowMessageRequest() {
|
||||||
return displayState.getValue() == DisplayState.DISPLAY_MESSAGE_REQUEST;
|
return displayState.getValue() == DisplayState.DISPLAY_MESSAGE_REQUEST;
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
@MainThread
|
||||||
public void onAccept(@NonNull GroupChangeErrorCallback error) {
|
public void onAccept() {
|
||||||
repository.acceptMessageRequest(liveRecipient, threadId, () -> {
|
status.setValue(Status.ACCEPTING);
|
||||||
status.postValue(Status.ACCEPTED);
|
repository.acceptMessageRequest(liveRecipient,
|
||||||
},
|
threadId,
|
||||||
error);
|
() -> status.postValue(Status.ACCEPTED),
|
||||||
|
this::onGroupChangeError);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
@MainThread
|
||||||
public void onDelete() {
|
public void onDelete() {
|
||||||
repository.deleteMessageRequest(liveRecipient, threadId, () -> {
|
status.setValue(Status.DELETING);
|
||||||
status.postValue(Status.DELETED);
|
repository.deleteMessageRequest(liveRecipient,
|
||||||
});
|
threadId,
|
||||||
|
() -> status.postValue(Status.DELETED),
|
||||||
|
this::onGroupChangeError);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
@MainThread
|
||||||
public void onBlock() {
|
public void onBlock() {
|
||||||
repository.blockMessageRequest(liveRecipient, () -> {
|
status.setValue(Status.BLOCKING);
|
||||||
status.postValue(Status.BLOCKED);
|
repository.blockMessageRequest(liveRecipient,
|
||||||
});
|
() -> status.postValue(Status.BLOCKED),
|
||||||
|
this::onGroupChangeError);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
@MainThread
|
||||||
public void onUnblock() {
|
public void onUnblock() {
|
||||||
repository.unblockAndAccept(liveRecipient, threadId, () -> {
|
repository.unblockAndAccept(liveRecipient,
|
||||||
status.postValue(Status.ACCEPTED);
|
threadId,
|
||||||
});
|
() -> status.postValue(Status.ACCEPTED));
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
@MainThread
|
||||||
public void onBlockAndDelete() {
|
public void onBlockAndDelete() {
|
||||||
repository.blockAndDeleteMessageRequest(liveRecipient, threadId, () -> {
|
repository.blockAndDeleteMessageRequest(liveRecipient,
|
||||||
status.postValue(Status.BLOCKED);
|
threadId,
|
||||||
});
|
() -> status.postValue(Status.BLOCKED),
|
||||||
|
this::onGroupChangeError);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onGroupChangeError(@NonNull GroupChangeFailureReason error) {
|
||||||
|
status.postValue(Status.IDLE);
|
||||||
|
failures.postValue(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadRecipient() {
|
private void loadRecipient() {
|
||||||
|
@ -191,8 +205,12 @@ public class MessageRequestViewModel extends ViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Status {
|
public enum Status {
|
||||||
|
IDLE,
|
||||||
|
BLOCKING,
|
||||||
BLOCKED,
|
BLOCKED,
|
||||||
|
DELETING,
|
||||||
DELETED,
|
DELETED,
|
||||||
|
ACCEPTING,
|
||||||
ACCEPTED
|
ACCEPTED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,19 +12,24 @@ import androidx.core.text.HtmlCompat;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.util.Debouncer;
|
||||||
import org.thoughtcrime.securesms.util.HtmlUtil;
|
import org.thoughtcrime.securesms.util.HtmlUtil;
|
||||||
|
|
||||||
public class MessageRequestsBottomView extends ConstraintLayout {
|
public class MessageRequestsBottomView extends ConstraintLayout {
|
||||||
|
|
||||||
|
private final Debouncer showProgressDebouncer = new Debouncer(250);
|
||||||
|
|
||||||
private TextView question;
|
private TextView question;
|
||||||
private View accept;
|
private View accept;
|
||||||
private View block;
|
private View block;
|
||||||
private View delete;
|
private View delete;
|
||||||
private View bigDelete;
|
private View bigDelete;
|
||||||
private View bigUnblock;
|
private View bigUnblock;
|
||||||
|
private View busyIndicator;
|
||||||
|
|
||||||
private Group normalButtons;
|
private Group normalButtons;
|
||||||
private Group blockedButtons;
|
private Group blockedButtons;
|
||||||
|
private Group activeGroup;
|
||||||
|
|
||||||
public MessageRequestsBottomView(Context context) {
|
public MessageRequestsBottomView(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
|
@ -52,6 +57,7 @@ public class MessageRequestsBottomView extends ConstraintLayout {
|
||||||
bigUnblock = findViewById(R.id.message_request_big_unblock);
|
bigUnblock = findViewById(R.id.message_request_big_unblock);
|
||||||
normalButtons = findViewById(R.id.message_request_normal_buttons);
|
normalButtons = findViewById(R.id.message_request_normal_buttons);
|
||||||
blockedButtons = findViewById(R.id.message_request_blocked_buttons);
|
blockedButtons = findViewById(R.id.message_request_blocked_buttons);
|
||||||
|
busyIndicator = findViewById(R.id.message_request_busy_indicator);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRecipient(@NonNull Recipient recipient) {
|
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();
|
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));
|
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);
|
setActiveInactiveGroups(blockedButtons, normalButtons);
|
||||||
blockedButtons.setVisibility(VISIBLE);
|
|
||||||
} else {
|
} else {
|
||||||
if (recipient.isGroup()) {
|
if (recipient.isGroup()) {
|
||||||
if (recipient.isPushV2Group()) {
|
if (recipient.isPushV2Group()) {
|
||||||
|
@ -75,8 +80,31 @@ public class MessageRequestsBottomView extends ConstraintLayout {
|
||||||
String name = recipient.getProfileName().isEmpty() ? recipient.getDisplayName(getContext()) : recipient.getProfileName().getGivenName();
|
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));
|
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);
|
setActiveInactiveGroups(normalButtons, blockedButtons);
|
||||||
blockedButtons.setVisibility(GONE);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package org.thoughtcrime.securesms.recipients;
|
package org.thoughtcrime.securesms.recipients;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
@ -9,12 +8,13 @@ import androidx.annotation.WorkerThread;
|
||||||
|
|
||||||
import com.annimon.stream.Stream;
|
import com.annimon.stream.Stream;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
|
||||||
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
|
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
|
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
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.groups.GroupManager;
|
||||||
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
|
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
|
||||||
|
@ -66,8 +66,32 @@ public class RecipientUtil {
|
||||||
return resolved.isPushGroup() || resolved.hasServiceIdentifier();
|
return resolved.isPushGroup() || resolved.hasServiceIdentifier();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* You can call this for non-groups and not have to handle any network errors.
|
||||||
|
*/
|
||||||
@WorkerThread
|
@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.
|
||||||
|
* <p>
|
||||||
|
* 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)) {
|
if (!isBlockable(recipient)) {
|
||||||
throw new AssertionError("Recipient is not blockable!");
|
throw new AssertionError("Recipient is not blockable!");
|
||||||
}
|
}
|
||||||
|
@ -76,8 +100,8 @@ public class RecipientUtil {
|
||||||
|
|
||||||
DatabaseFactory.getRecipientDatabase(context).setBlocked(resolved.getId(), true);
|
DatabaseFactory.getRecipientDatabase(context).setBlocked(resolved.getId(), true);
|
||||||
|
|
||||||
if (resolved.isGroup()) {
|
if (resolved.isGroup() && recipient.getGroupId().get().isPush()) {
|
||||||
leaveGroup(context, recipient);
|
GroupManager.leaveGroupFromBlockOrMessageRequest(context, recipient.getGroupId().get().requirePush());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resolved.isSystemContact() || resolved.isProfileSharing() || isProfileSharedViaGroup(context,resolved)) {
|
if (resolved.isSystemContact() || resolved.isProfileSharing() || isProfileSharedViaGroup(context,resolved)) {
|
||||||
|
@ -101,20 +125,6 @@ public class RecipientUtil {
|
||||||
ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forAccept(recipient.getId()));
|
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
|
* If true, the new message request UI does not need to be shown, and it's safe to send read
|
||||||
* receipts.
|
* receipts.
|
||||||
|
|
|
@ -120,7 +120,7 @@ final class RecipientDialogViewModel extends ViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
void onBlockClicked(@NonNull FragmentActivity activity) {
|
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) {
|
void onUnblockClicked(@NonNull FragmentActivity activity) {
|
||||||
|
|
|
@ -210,7 +210,7 @@ public final class ManageRecipientViewModel extends ViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
void onBlockClicked(@NonNull FragmentActivity activity) {
|
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) {
|
void onUnblockClicked(@NonNull FragmentActivity activity) {
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_marginBottom="11dp"
|
android:layout_marginBottom="11dp"
|
||||||
android:textAppearance="@style/Signal.Text.MessageRequest.Description"
|
|
||||||
android:paddingTop="16dp"
|
android:paddingTop="16dp"
|
||||||
|
android:textAppearance="@style/Signal.Text.MessageRequest.Description"
|
||||||
app:layout_constraintBottom_toTopOf="@id/message_request_button_barrier"
|
app:layout_constraintBottom_toTopOf="@id/message_request_button_barrier"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
@ -20,12 +20,12 @@
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/message_request_block"
|
android:id="@+id/message_request_block"
|
||||||
|
style="@style/Signal.MessageRequest.Button.Deny"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
style="@style/Signal.MessageRequest.Button.Deny"
|
|
||||||
android:text="@string/MessageRequestBottomView_block"
|
android:text="@string/MessageRequestBottomView_block"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/message_request_delete"
|
app:layout_constraintEnd_toStartOf="@+id/message_request_delete"
|
||||||
|
@ -34,10 +34,10 @@
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/message_request_delete"
|
android:id="@+id/message_request_delete"
|
||||||
|
style="@style/Signal.MessageRequest.Button.Deny"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
style="@style/Signal.MessageRequest.Button.Deny"
|
|
||||||
android:text="@string/MessageRequestBottomView_delete"
|
android:text="@string/MessageRequestBottomView_delete"
|
||||||
app:layout_constraintBottom_toBottomOf="@id/message_request_block"
|
app:layout_constraintBottom_toBottomOf="@id/message_request_block"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/message_request_accept"
|
app:layout_constraintEnd_toStartOf="@+id/message_request_accept"
|
||||||
|
@ -47,10 +47,10 @@
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/message_request_accept"
|
android:id="@+id/message_request_accept"
|
||||||
|
style="@style/Signal.MessageRequest.Button.Accept"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
style="@style/Signal.MessageRequest.Button.Accept"
|
|
||||||
android:text="@string/MessageRequestBottomView_accept"
|
android:text="@string/MessageRequestBottomView_accept"
|
||||||
app:layout_constraintBottom_toBottomOf="@id/message_request_block"
|
app:layout_constraintBottom_toBottomOf="@id/message_request_block"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
@ -60,35 +60,35 @@
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/message_request_big_delete"
|
android:id="@+id/message_request_big_delete"
|
||||||
|
style="@style/Signal.MessageRequest.Button.Deny"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
style="@style/Signal.MessageRequest.Button.Deny"
|
|
||||||
android:text="@string/MessageRequestBottomView_delete"
|
android:text="@string/MessageRequestBottomView_delete"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintEnd_toStartOf="@id/message_request_big_unblock"
|
||||||
app:layout_constraintEnd_toStartOf="@id/message_request_big_unblock"/>
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/message_request_big_unblock"
|
android:id="@+id/message_request_big_unblock"
|
||||||
|
style="@style/Signal.MessageRequest.Button.Accept"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
style="@style/Signal.MessageRequest.Button.Accept"
|
|
||||||
android:text="@string/MessageRequestBottomView_unblock"
|
android:text="@string/MessageRequestBottomView_unblock"
|
||||||
app:layout_constraintTop_toTopOf="@id/message_request_big_delete"
|
app:layout_constraintBottom_toBottomOf="@id/message_request_big_delete"
|
||||||
app:layout_constraintStart_toEndOf="@id/message_request_big_delete"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintBottom_toBottomOf="@id/message_request_big_delete"/>
|
app:layout_constraintStart_toEndOf="@id/message_request_big_delete"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/message_request_big_delete" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Barrier
|
<androidx.constraintlayout.widget.Barrier
|
||||||
android:id="@+id/message_request_button_barrier"
|
android:id="@+id/message_request_button_barrier"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
app:barrierDirection="top"
|
app:barrierDirection="top"
|
||||||
app:constraint_referenced_ids="message_request_block,message_request_big_delete"/>
|
app:constraint_referenced_ids="message_request_block,message_request_big_delete" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Group
|
<androidx.constraintlayout.widget.Group
|
||||||
android:id="@+id/message_request_normal_buttons"
|
android:id="@+id/message_request_normal_buttons"
|
||||||
|
@ -102,6 +102,20 @@
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible"
|
app:constraint_referenced_ids="message_request_big_delete,message_request_big_unblock"
|
||||||
app:constraint_referenced_ids="message_request_big_delete,message_request_big_unblock" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/message_request_busy_indicator"
|
||||||
|
style="?android:attr/progressBarStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/message_request_question"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
</merge>
|
</merge>
|
Loading…
Reference in New Issue