mirror of
https://github.com/oxen-io/session-android.git
synced 2025-06-08 19:58:33 +00:00
Adapt message requests to support invite flow.
This commit is contained in:
parent
d3d53e6099
commit
eff564ad88
@ -153,6 +153,8 @@ import org.thoughtcrime.securesms.groups.GroupChangeFailedException;
|
|||||||
import org.thoughtcrime.securesms.groups.GroupInsufficientRightsException;
|
import org.thoughtcrime.securesms.groups.GroupInsufficientRightsException;
|
||||||
import org.thoughtcrime.securesms.groups.GroupManager;
|
import org.thoughtcrime.securesms.groups.GroupManager;
|
||||||
import org.thoughtcrime.securesms.groups.GroupNotAMemberException;
|
import org.thoughtcrime.securesms.groups.GroupNotAMemberException;
|
||||||
|
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
|
||||||
|
import org.thoughtcrime.securesms.groups.ui.GroupErrors;
|
||||||
import org.thoughtcrime.securesms.groups.ui.LeaveGroupDialog;
|
import org.thoughtcrime.securesms.groups.ui.LeaveGroupDialog;
|
||||||
import org.thoughtcrime.securesms.groups.ui.managegroup.ManageGroupActivity;
|
import org.thoughtcrime.securesms.groups.ui.managegroup.ManageGroupActivity;
|
||||||
import org.thoughtcrime.securesms.groups.ui.pendingmemberinvites.PendingMemberInvitesActivity;
|
import org.thoughtcrime.securesms.groups.ui.pendingmemberinvites.PendingMemberInvitesActivity;
|
||||||
@ -2190,6 +2192,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
return getRecipient() != null && getRecipient().isPushGroup();
|
return getRecipient() != null && getRecipient().isPushGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isPushGroupV1Conversation() {
|
||||||
|
return getRecipient() != null && getRecipient().isPushV1Group();
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isSmsForced() {
|
private boolean isSmsForced() {
|
||||||
return sendButton.isManualSelection() && sendButton.getSelectedTransport().isSms();
|
return sendButton.isManualSelection() && sendButton.getSelectedTransport().isSms();
|
||||||
}
|
}
|
||||||
@ -2825,7 +2831,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessageRequest(@NonNull MessageRequestViewModel viewModel) {
|
public void onMessageRequest(@NonNull MessageRequestViewModel viewModel) {
|
||||||
messageRequestBottomView.setAcceptOnClickListener(v -> viewModel.onAccept());
|
messageRequestBottomView.setAcceptOnClickListener(v -> viewModel.onAccept(this::showGroupChangeErrorToast));
|
||||||
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));
|
||||||
@ -2844,6 +2850,10 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showGroupChangeErrorToast(@NonNull GroupChangeFailureReason e) {
|
||||||
|
Toast.makeText(this, GroupErrors.getUserDisplayMessage(e), Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleReaction(@NonNull View maskTarget,
|
public void handleReaction(@NonNull View maskTarget,
|
||||||
@NonNull MessageRecord messageRecord,
|
@NonNull MessageRecord messageRecord,
|
||||||
@ -3005,9 +3015,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void presentMessageRequestDisplayState(@NonNull MessageRequestViewModel.DisplayState displayState) {
|
private void presentMessageRequestDisplayState(@NonNull MessageRequestViewModel.DisplayState displayState) {
|
||||||
if (getIntent().hasExtra(TEXT_EXTRA) || getIntent().hasExtra(MEDIA_EXTRA) || getIntent().hasExtra(STICKER_EXTRA) || (isPushGroupConversation() && !isActiveGroup())) {
|
if (getIntent().hasExtra(TEXT_EXTRA) || getIntent().hasExtra(MEDIA_EXTRA) || getIntent().hasExtra(STICKER_EXTRA)) {
|
||||||
Log.d(TAG, "[presentMessageRequestDisplayState] Have extra, so ignoring provided state.");
|
Log.d(TAG, "[presentMessageRequestDisplayState] Have extra, so ignoring provided state.");
|
||||||
messageRequestBottomView.setVisibility(View.GONE);
|
messageRequestBottomView.setVisibility(View.GONE);
|
||||||
|
} else if (isPushGroupV1Conversation() && !isActiveGroup()) {
|
||||||
|
Log.d(TAG, "[presentMessageRequestDisplayState] Inactive push group V1, so ignoring provided state.");
|
||||||
|
messageRequestBottomView.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
Log.d(TAG, "[presentMessageRequestDisplayState] " + displayState);
|
Log.d(TAG, "[presentMessageRequestDisplayState] " + displayState);
|
||||||
switch (displayState) {
|
switch (displayState) {
|
||||||
|
@ -496,6 +496,11 @@ public final class GroupDatabase extends Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
public boolean isPendingMember(@NonNull GroupId.Push groupId, @NonNull Recipient recipient) {
|
||||||
|
return getGroup(groupId).transform(g -> g.isPendingMember(recipient)).or(false);
|
||||||
|
}
|
||||||
|
|
||||||
private static String serializeV2GroupMembers(@NonNull Context context, @NonNull DecryptedGroup decryptedGroup) {
|
private static String serializeV2GroupMembers(@NonNull Context context, @NonNull DecryptedGroup decryptedGroup) {
|
||||||
List<RecipientId> groupMembers = new ArrayList<>(decryptedGroup.getMembersCount());
|
List<RecipientId> groupMembers = new ArrayList<>(decryptedGroup.getMembersCount());
|
||||||
|
|
||||||
@ -707,6 +712,17 @@ public final class GroupDatabase extends Database {
|
|||||||
return id.isV1() ? GroupAccessControl.ALL_MEMBERS : GroupAccessControl.ONLY_ADMINS;
|
return id.isV1() ? GroupAccessControl.ALL_MEMBERS : GroupAccessControl.ONLY_ADMINS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isPendingMember(@NonNull Recipient recipient) {
|
||||||
|
if (isV2Group()) {
|
||||||
|
Optional<UUID> uuid = recipient.getUuid();
|
||||||
|
if (uuid.isPresent()) {
|
||||||
|
return DecryptedGroupUtil.findPendingByUuid(requireV2GroupProperties().getDecryptedGroup().getPendingMembersList(), uuid.get())
|
||||||
|
.isPresent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class V2GroupProperties {
|
public static class V2GroupProperties {
|
||||||
|
@ -31,6 +31,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
|||||||
|
|
||||||
import net.sqlcipher.database.SQLiteDatabase;
|
import net.sqlcipher.database.SQLiteDatabase;
|
||||||
|
|
||||||
|
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||||
import org.thoughtcrime.securesms.contactshare.Contact;
|
import org.thoughtcrime.securesms.contactshare.Contact;
|
||||||
import org.thoughtcrime.securesms.contactshare.ContactUtil;
|
import org.thoughtcrime.securesms.contactshare.ContactUtil;
|
||||||
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
|
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
|
||||||
@ -51,6 +52,7 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.whispersystems.libsignal.util.Pair;
|
import org.whispersystems.libsignal.util.Pair;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -61,6 +63,7 @@ import java.util.LinkedList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class ThreadDatabase extends Database {
|
public class ThreadDatabase extends Database {
|
||||||
|
|
||||||
@ -761,12 +764,21 @@ public class ThreadDatabase extends Database {
|
|||||||
RecipientId threadRecipientId = getRecipientIdForThreadId(record.getThreadId());
|
RecipientId threadRecipientId = getRecipientIdForThreadId(record.getThreadId());
|
||||||
|
|
||||||
if (!messageRequestAccepted && threadRecipientId != null) {
|
if (!messageRequestAccepted && threadRecipientId != null) {
|
||||||
boolean isPushGroup = Recipient.resolved(threadRecipientId).isPushGroup();
|
Recipient resolved = Recipient.resolved(threadRecipientId);
|
||||||
if (isPushGroup) {
|
if (resolved.isPushGroup()) {
|
||||||
RecipientId recipientId = DatabaseFactory.getMmsSmsDatabase(context).getGroupAddedBy(record.getThreadId());
|
if (resolved.isPushV2Group()) {
|
||||||
|
DecryptedGroup decryptedGroup = DatabaseFactory.getGroupDatabase(context).requireGroup(resolved.requireGroupId().requireV2()).requireV2GroupProperties().getDecryptedGroup();
|
||||||
|
Optional<UUID> inviter = DecryptedGroupUtil.findInviter(decryptedGroup.getPendingMembersList(), Recipient.self().getUuid().get());
|
||||||
|
|
||||||
if (recipientId != null) {
|
RecipientId recipientId = inviter.isPresent() ? RecipientId.from(inviter.get(), null) : RecipientId.UNKNOWN;
|
||||||
return Extra.forGroupMessageRequest(recipientId);
|
|
||||||
|
return Extra.forGroupV2invite(recipientId);
|
||||||
|
} else {
|
||||||
|
RecipientId recipientId = DatabaseFactory.getMmsSmsDatabase(context).getGroupAddedBy(record.getThreadId());
|
||||||
|
|
||||||
|
if (recipientId != null) {
|
||||||
|
return Extra.forGroupMessageRequest(recipientId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -903,6 +915,7 @@ public class ThreadDatabase extends Database {
|
|||||||
@JsonProperty private final boolean isAlbum;
|
@JsonProperty private final boolean isAlbum;
|
||||||
@JsonProperty private final boolean isRemoteDelete;
|
@JsonProperty private final boolean isRemoteDelete;
|
||||||
@JsonProperty private final boolean isMessageRequestAccepted;
|
@JsonProperty private final boolean isMessageRequestAccepted;
|
||||||
|
@JsonProperty private final boolean isGv2Invite;
|
||||||
@JsonProperty private final String groupAddedBy;
|
@JsonProperty private final String groupAddedBy;
|
||||||
|
|
||||||
public Extra(@JsonProperty("isRevealable") boolean isRevealable,
|
public Extra(@JsonProperty("isRevealable") boolean isRevealable,
|
||||||
@ -910,6 +923,7 @@ public class ThreadDatabase extends Database {
|
|||||||
@JsonProperty("isAlbum") boolean isAlbum,
|
@JsonProperty("isAlbum") boolean isAlbum,
|
||||||
@JsonProperty("isRemoteDelete") boolean isRemoteDelete,
|
@JsonProperty("isRemoteDelete") boolean isRemoteDelete,
|
||||||
@JsonProperty("isMessageRequestAccepted") boolean isMessageRequestAccepted,
|
@JsonProperty("isMessageRequestAccepted") boolean isMessageRequestAccepted,
|
||||||
|
@JsonProperty("isGv2Invite") boolean isGv2Invite,
|
||||||
@JsonProperty("groupAddedBy") String groupAddedBy)
|
@JsonProperty("groupAddedBy") String groupAddedBy)
|
||||||
{
|
{
|
||||||
this.isRevealable = isRevealable;
|
this.isRevealable = isRevealable;
|
||||||
@ -917,31 +931,36 @@ public class ThreadDatabase extends Database {
|
|||||||
this.isAlbum = isAlbum;
|
this.isAlbum = isAlbum;
|
||||||
this.isRemoteDelete = isRemoteDelete;
|
this.isRemoteDelete = isRemoteDelete;
|
||||||
this.isMessageRequestAccepted = isMessageRequestAccepted;
|
this.isMessageRequestAccepted = isMessageRequestAccepted;
|
||||||
|
this.isGv2Invite = isGv2Invite;
|
||||||
this.groupAddedBy = groupAddedBy;
|
this.groupAddedBy = groupAddedBy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NonNull Extra forViewOnce() {
|
public static @NonNull Extra forViewOnce() {
|
||||||
return new Extra(true, false, false, false, true, null);
|
return new Extra(true, false, false, false, true, false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NonNull Extra forSticker() {
|
public static @NonNull Extra forSticker() {
|
||||||
return new Extra(false, true, false, false, true, null);
|
return new Extra(false, true, false, false, true, false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NonNull Extra forAlbum() {
|
public static @NonNull Extra forAlbum() {
|
||||||
return new Extra(false, false, true, false, true, null);
|
return new Extra(false, false, true, false, true, false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NonNull Extra forRemoteDelete() {
|
public static @NonNull Extra forRemoteDelete() {
|
||||||
return new Extra(false, false, false, true, true, null);
|
return new Extra(false, false, false, true, true, false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NonNull Extra forMessageRequest() {
|
public static @NonNull Extra forMessageRequest() {
|
||||||
return new Extra(false, false, false, false, false, null);
|
return new Extra(false, false, false, false, false, false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NonNull Extra forGroupMessageRequest(RecipientId recipientId) {
|
public static @NonNull Extra forGroupMessageRequest(RecipientId recipientId) {
|
||||||
return new Extra(false, false, false, false, false, recipientId.serialize());
|
return new Extra(false, false, false, false, false, false, recipientId.serialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NonNull Extra forGroupV2invite(RecipientId recipientId) {
|
||||||
|
return new Extra(false, false, false, false, false, true, recipientId.serialize());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isViewOnce() {
|
public boolean isViewOnce() {
|
||||||
@ -964,6 +983,10 @@ public class ThreadDatabase extends Database {
|
|||||||
return isMessageRequestAccepted;
|
return isMessageRequestAccepted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isGv2Invite() {
|
||||||
|
return isGv2Invite;
|
||||||
|
}
|
||||||
|
|
||||||
public @Nullable String getGroupAddedBy() {
|
public @Nullable String getGroupAddedBy() {
|
||||||
return groupAddedBy;
|
return groupAddedBy;
|
||||||
}
|
}
|
||||||
|
@ -19,13 +19,14 @@ package org.thoughtcrime.securesms.database.model;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import android.text.Spannable;
|
import android.text.Spannable;
|
||||||
import android.text.SpannableString;
|
import android.text.SpannableString;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.style.StyleSpan;
|
import android.text.style.StyleSpan;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.database.MmsSmsColumns;
|
import org.thoughtcrime.securesms.database.MmsSmsColumns;
|
||||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||||
@ -79,7 +80,9 @@ public class ThreadRecord extends DisplayRecord {
|
|||||||
@Override
|
@Override
|
||||||
public SpannableString getDisplayBody(@NonNull Context context) {
|
public SpannableString getDisplayBody(@NonNull Context context) {
|
||||||
if (getGroupAddedBy() != null) {
|
if (getGroupAddedBy() != null) {
|
||||||
return emphasisAdded(context.getString(R.string.ThreadRecord_s_added_you_to_the_group, Recipient.live(getGroupAddedBy()).get().getDisplayName(context)));
|
return emphasisAdded(context.getString(isGv2Invite() ? R.string.ThreadRecord_s_invited_you_to_the_group
|
||||||
|
: R.string.ThreadRecord_s_added_you_to_the_group,
|
||||||
|
Recipient.live(getGroupAddedBy()).get().getDisplayName(context)));
|
||||||
} else if (!isMessageRequestAccepted()) {
|
} else if (!isMessageRequestAccepted()) {
|
||||||
return emphasisAdded(context.getString(R.string.ThreadRecord_message_request));
|
return emphasisAdded(context.getString(R.string.ThreadRecord_message_request));
|
||||||
} else if (isGroupUpdate()) {
|
} else if (isGroupUpdate()) {
|
||||||
@ -194,6 +197,10 @@ public class ThreadRecord extends DisplayRecord {
|
|||||||
else return null;
|
else return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isGv2Invite() {
|
||||||
|
return extra != null && extra.isGv2Invite();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isMessageRequestAccepted() {
|
public boolean isMessageRequestAccepted() {
|
||||||
if (extra != null) return extra.isMessageRequestAccepted();
|
if (extra != null) return extra.isMessageRequestAccepted();
|
||||||
else return true;
|
else return true;
|
||||||
|
@ -183,6 +183,8 @@ public final class GroupManager {
|
|||||||
{
|
{
|
||||||
try (GroupManagerV2.GroupEditor editor = new GroupManagerV2(context).edit(groupId.requireV2())) {
|
try (GroupManagerV2.GroupEditor editor = new GroupManagerV2(context).edit(groupId.requireV2())) {
|
||||||
editor.acceptInvite();
|
editor.acceptInvite();
|
||||||
|
DatabaseFactory.getGroupDatabase(context)
|
||||||
|
.setActive(groupId, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,9 @@ public class ProfileKeySendJob extends BaseJob {
|
|||||||
private final long threadId;
|
private final long threadId;
|
||||||
private final List<RecipientId> recipients;
|
private final List<RecipientId> recipients;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Suitable for a 1:1 conversation or a GV1 group only.
|
||||||
|
*/
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
public static ProfileKeySendJob create(@NonNull Context context, long threadId) {
|
public static ProfileKeySendJob create(@NonNull Context context, long threadId) {
|
||||||
Recipient conversationRecipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId);
|
Recipient conversationRecipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId);
|
||||||
@ -49,6 +52,10 @@ public class ProfileKeySendJob extends BaseJob {
|
|||||||
throw new AssertionError("We have a thread but no recipient!");
|
throw new AssertionError("We have a thread but no recipient!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (conversationRecipient.isPushV2Group()) {
|
||||||
|
throw new AssertionError("Do not send profile keys directly for GV2");
|
||||||
|
}
|
||||||
|
|
||||||
List<RecipientId> recipients = conversationRecipient.isGroup() ? Stream.of(conversationRecipient.getParticipants()).map(Recipient::getId).toList()
|
List<RecipientId> recipients = conversationRecipient.isGroup() ? Stream.of(conversationRecipient.getParticipants()).map(Recipient::getId).toList()
|
||||||
: Stream.of(conversationRecipient.getId()).toList();
|
: Stream.of(conversationRecipient.getId()).toList();
|
||||||
|
|
||||||
|
@ -5,13 +5,22 @@ import android.content.Context;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.core.util.Consumer;
|
import androidx.core.util.Consumer;
|
||||||
|
|
||||||
|
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
import org.thoughtcrime.securesms.database.MessagingDatabase;
|
import org.thoughtcrime.securesms.database.MessagingDatabase;
|
||||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||||
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.GroupInsufficientRightsException;
|
||||||
|
import org.thoughtcrime.securesms.groups.GroupManager;
|
||||||
|
import org.thoughtcrime.securesms.groups.GroupNotAMemberException;
|
||||||
|
import org.thoughtcrime.securesms.groups.ui.GroupChangeErrorCallback;
|
||||||
|
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceMessageRequestResponseJob;
|
import org.thoughtcrime.securesms.jobs.MultiDeviceMessageRequestResponseJob;
|
||||||
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
|
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
|
||||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||||
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
||||||
@ -20,14 +29,18 @@ 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;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
final class MessageRequestRepository {
|
final class MessageRequestRepository {
|
||||||
|
|
||||||
|
private static final String TAG = Log.tag(MessageRequestRepository.class);
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final Executor executor;
|
private final Executor executor;
|
||||||
|
|
||||||
@ -48,14 +61,24 @@ final class MessageRequestRepository {
|
|||||||
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()) {
|
||||||
|
DecryptedGroup decryptedGroup = record.requireV2GroupProperties().getDecryptedGroup();
|
||||||
|
return new GroupMemberCount(decryptedGroup.getMembersCount(), decryptedGroup.getPendingMembersCount());
|
||||||
|
} else {
|
||||||
return new GroupMemberCount(record.getMembers().size(), 0);
|
return new GroupMemberCount(record.getMembers().size(), 0);
|
||||||
|
}
|
||||||
}).or(GroupMemberCount.ZERO));
|
}).or(GroupMemberCount.ZERO));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void getMessageRequestState(@NonNull Recipient recipient, long threadId, @NonNull Consumer<MessageRequestState> state) {
|
void getMessageRequestState(@NonNull Recipient recipient, long threadId, @NonNull Consumer<MessageRequestState> state) {
|
||||||
executor.execute(() -> {
|
executor.execute(() -> {
|
||||||
if (!RecipientUtil.isMessageRequestAccepted(context, threadId)) {
|
if (recipient.isPushV2Group()) {
|
||||||
|
boolean pendingMember = DatabaseFactory.getGroupDatabase(context)
|
||||||
|
.isPendingMember(recipient.requireGroupId().requireV2(), Recipient.self());
|
||||||
|
state.accept(pendingMember ? MessageRequestState.UNACCEPTED
|
||||||
|
: MessageRequestState.ACCEPTED);
|
||||||
|
} else if (!RecipientUtil.isMessageRequestAccepted(context, threadId)) {
|
||||||
state.accept(MessageRequestState.UNACCEPTED);
|
state.accept(MessageRequestState.UNACCEPTED);
|
||||||
} else if (RecipientUtil.isPreMessageRequestThread(context, threadId) && !RecipientUtil.isLegacyProfileSharingAccepted(recipient)) {
|
} else if (RecipientUtil.isPreMessageRequestThread(context, threadId) && !RecipientUtil.isLegacyProfileSharingAccepted(recipient)) {
|
||||||
state.accept(MessageRequestState.LEGACY);
|
state.accept(MessageRequestState.LEGACY);
|
||||||
@ -65,23 +88,46 @@ final class MessageRequestRepository {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void acceptMessageRequest(@NonNull LiveRecipient liveRecipient, long threadId, @NonNull Runnable onMessageRequestAccepted) {
|
void acceptMessageRequest(@NonNull LiveRecipient liveRecipient,
|
||||||
|
long threadId,
|
||||||
|
@NonNull Runnable onMessageRequestAccepted,
|
||||||
|
@NonNull GroupChangeErrorCallback mainThreadError)
|
||||||
|
{
|
||||||
|
GroupChangeErrorCallback error = e -> Util.runOnMain(() -> mainThreadError.onError(e));
|
||||||
executor.execute(()-> {
|
executor.execute(()-> {
|
||||||
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
if (liveRecipient.get().isPushV2Group()) {
|
||||||
recipientDatabase.setProfileSharing(liveRecipient.getId(), true);
|
try {
|
||||||
liveRecipient.refresh();
|
Log.i(TAG, "GV2 accepting invite");
|
||||||
|
GroupManager.acceptInvite(context, liveRecipient.get().requireGroupId().requireV2());
|
||||||
|
|
||||||
List<MessagingDatabase.MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(context)
|
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
||||||
.setEntireThreadRead(threadId);
|
recipientDatabase.setProfileSharing(liveRecipient.getId(), true);
|
||||||
MessageNotifier.updateNotification(context);
|
|
||||||
MarkReadReceiver.process(context, messageIds);
|
|
||||||
|
|
||||||
if (TextSecurePreferences.isMultiDevice(context)) {
|
onMessageRequestAccepted.run();
|
||||||
ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forAccept(liveRecipient.getId()));
|
} catch (GroupInsufficientRightsException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
error.onError(GroupChangeFailureReason.NO_RIGHTS);
|
||||||
|
} catch (GroupChangeBusyException | GroupChangeFailedException | GroupNotAMemberException | IOException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
error.onError(GroupChangeFailureReason.OTHER);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
||||||
|
recipientDatabase.setProfileSharing(liveRecipient.getId(), true);
|
||||||
|
|
||||||
|
MessageSender.sendProfileKey(context, threadId);
|
||||||
|
|
||||||
|
List<MessagingDatabase.MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(context)
|
||||||
|
.setEntireThreadRead(threadId);
|
||||||
|
MessageNotifier.updateNotification(context);
|
||||||
|
MarkReadReceiver.process(context, messageIds);
|
||||||
|
|
||||||
|
if (TextSecurePreferences.isMultiDevice(context)) {
|
||||||
|
ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forAccept(liveRecipient.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
onMessageRequestAccepted.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageSender.sendProfileKey(context, threadId);
|
|
||||||
onMessageRequestAccepted.run();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ 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.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;
|
||||||
@ -89,10 +90,11 @@ public class MessageRequestViewModel extends ViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
@MainThread
|
||||||
public void onAccept() {
|
public void onAccept(@NonNull GroupChangeErrorCallback error) {
|
||||||
repository.acceptMessageRequest(liveRecipient, threadId, () -> {
|
repository.acceptMessageRequest(liveRecipient, threadId, () -> {
|
||||||
status.postValue(Status.ACCEPTED);
|
status.postValue(Status.ACCEPTED);
|
||||||
});
|
},
|
||||||
|
error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
@MainThread
|
||||||
|
@ -65,7 +65,11 @@ public class MessageRequestsBottomView extends ConstraintLayout {
|
|||||||
blockedButtons.setVisibility(VISIBLE);
|
blockedButtons.setVisibility(VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
if (recipient.isGroup()) {
|
if (recipient.isGroup()) {
|
||||||
question.setText(HtmlCompat.fromHtml(getContext().getString(R.string.MessageRequestBottomView_do_you_want_to_join_the_group_s_they_wont_know_youve_seen_their_messages_until_you_accept, HtmlUtil.bold(recipient.getDisplayName(getContext()))), 0));
|
if (recipient.isPushV2Group()) {
|
||||||
|
question.setText(HtmlCompat.fromHtml(getContext().getString(R.string.MessageRequestBottomView_you_were_invited_to_join_the_group_s, HtmlUtil.bold(recipient.getDisplayName(getContext()))), 0));
|
||||||
|
} else {
|
||||||
|
question.setText(HtmlCompat.fromHtml(getContext().getString(R.string.MessageRequestBottomView_do_you_want_to_join_the_group_s_they_wont_know_youve_seen_their_messages_until_you_accept, HtmlUtil.bold(recipient.getDisplayName(getContext()))), 0));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
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(recipient.getDisplayName(getContext()))), 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(recipient.getDisplayName(getContext()))), 0));
|
||||||
}
|
}
|
||||||
|
@ -603,6 +603,11 @@ public class Recipient {
|
|||||||
return groupId != null && groupId.isPush();
|
return groupId != null && groupId.isPush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isPushV1Group() {
|
||||||
|
GroupId groupId = resolve().groupId;
|
||||||
|
return groupId != null && groupId.isV1();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isPushV2Group() {
|
public boolean isPushV2Group() {
|
||||||
GroupId groupId = resolve().groupId;
|
GroupId groupId = resolve().groupId;
|
||||||
return groupId != null && groupId.isV2();
|
return groupId != null && groupId.isV2();
|
||||||
|
@ -83,6 +83,9 @@ public class MessageSender {
|
|||||||
|
|
||||||
private static final String TAG = MessageSender.class.getSimpleName();
|
private static final String TAG = MessageSender.class.getSimpleName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Suitable for a 1:1 conversation or a GV1 group only.
|
||||||
|
*/
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
public static void sendProfileKey(final Context context, final long threadId) {
|
public static void sendProfileKey(final Context context, final long threadId) {
|
||||||
ApplicationDependencies.getJobManager().add(ProfileKeySendJob.create(context, threadId));
|
ApplicationDependencies.getJobManager().add(ProfileKeySendJob.create(context, threadId));
|
||||||
|
@ -774,6 +774,7 @@
|
|||||||
<string name="MessageRequestBottomView_unblock">Unblock</string>
|
<string name="MessageRequestBottomView_unblock">Unblock</string>
|
||||||
<string name="MessageRequestBottomView_do_you_want_to_let_s_message_you_they_wont_know_youve_seen_their_messages_until_you_accept">Do you want to let %1$s message you? They won\'t know you\'ve seen their messages until you accept.</string>
|
<string name="MessageRequestBottomView_do_you_want_to_let_s_message_you_they_wont_know_youve_seen_their_messages_until_you_accept">Do you want to let %1$s message you? They won\'t know you\'ve seen their messages until you accept.</string>
|
||||||
<string name="MessageRequestBottomView_do_you_want_to_join_the_group_s_they_wont_know_youve_seen_their_messages_until_you_accept">Do you want to join the group %1$s? They won\'t know you\'ve seen their messages until you accept.</string>
|
<string name="MessageRequestBottomView_do_you_want_to_join_the_group_s_they_wont_know_youve_seen_their_messages_until_you_accept">Do you want to join the group %1$s? They won\'t know you\'ve seen their messages until you accept.</string>
|
||||||
|
<string name="MessageRequestBottomView_you_were_invited_to_join_the_group_s">You were invited to join the group %1$s. Do you want to let members of this group message you?</string>
|
||||||
<string name="MessageRequestBottomView_unblock_s_to_message_and_call_each_other">Unblock %1$s to message and call each other.</string>
|
<string name="MessageRequestBottomView_unblock_s_to_message_and_call_each_other">Unblock %1$s to message and call each other.</string>
|
||||||
<string name="MessageRequestBottomView_unblock_to_allow_group_members_to_add_you_to_this_group_again">Unblock to allow group members to add you to this group again.</string>
|
<string name="MessageRequestBottomView_unblock_to_allow_group_members_to_add_you_to_this_group_again">Unblock to allow group members to add you to this group again.</string>
|
||||||
<string name="MessageRequestProfileView_member_of_one_group">Member of %1$s</string>
|
<string name="MessageRequestProfileView_member_of_one_group">Member of %1$s</string>
|
||||||
@ -1087,6 +1088,7 @@
|
|||||||
<string name="ThreadRecord_message_could_not_be_processed">Message could not be processed</string>
|
<string name="ThreadRecord_message_could_not_be_processed">Message could not be processed</string>
|
||||||
<string name="ThreadRecord_message_request">Message Request</string>
|
<string name="ThreadRecord_message_request">Message Request</string>
|
||||||
<string name="ThreadRecord_s_added_you_to_the_group">%1$s added you to the group</string>
|
<string name="ThreadRecord_s_added_you_to_the_group">%1$s added you to the group</string>
|
||||||
|
<string name="ThreadRecord_s_invited_you_to_the_group">%1$s invited you to the group</string>
|
||||||
|
|
||||||
<!-- UpdateApkReadyListener -->
|
<!-- UpdateApkReadyListener -->
|
||||||
<string name="UpdateApkReadyListener_Signal_update">Signal update</string>
|
<string name="UpdateApkReadyListener_Signal_update">Signal update</string>
|
||||||
|
@ -252,6 +252,12 @@ public final class DecryptedGroupUtil {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Optional<UUID> findInviter(List<DecryptedPendingMember> pendingMembersList, UUID uuid) {
|
||||||
|
return Optional.fromNullable(findPendingByUuid(pendingMembersList, uuid).transform(DecryptedPendingMember::getAddedByUuid)
|
||||||
|
.transform(UuidUtil::fromByteStringOrNull)
|
||||||
|
.orNull());
|
||||||
|
}
|
||||||
|
|
||||||
public static class NotAbleToApplyChangeException extends Throwable {
|
public static class NotAbleToApplyChangeException extends Throwable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user