254 lines
10 KiB
Java
254 lines
10 KiB
Java
package org.thoughtcrime.securesms.messagerequests;
|
|
|
|
import android.content.Context;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.WorkerThread;
|
|
import androidx.core.util.Consumer;
|
|
|
|
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
|
import org.thoughtcrime.securesms.database.MessageDatabase;
|
|
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
|
import org.thoughtcrime.securesms.groups.GroupChangeException;
|
|
import org.thoughtcrime.securesms.groups.GroupId;
|
|
import org.thoughtcrime.securesms.groups.GroupManager;
|
|
import org.thoughtcrime.securesms.groups.ui.GroupChangeErrorCallback;
|
|
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
|
|
import org.thoughtcrime.securesms.jobs.MultiDeviceMessageRequestResponseJob;
|
|
import org.thoughtcrime.securesms.logging.Log;
|
|
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
|
|
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
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.concurrent.SignalExecutors;
|
|
import org.whispersystems.libsignal.util.guava.Optional;
|
|
|
|
import java.io.IOException;
|
|
import java.util.List;
|
|
import java.util.concurrent.Executor;
|
|
|
|
final class MessageRequestRepository {
|
|
|
|
private static final String TAG = Log.tag(MessageRequestRepository.class);
|
|
|
|
private final Context context;
|
|
private final Executor executor;
|
|
|
|
MessageRequestRepository(@NonNull Context context) {
|
|
this.context = context.getApplicationContext();
|
|
this.executor = SignalExecutors.BOUNDED;
|
|
}
|
|
|
|
void getGroups(@NonNull RecipientId recipientId, @NonNull Consumer<List<String>> onGroupsLoaded) {
|
|
executor.execute(() -> {
|
|
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
|
onGroupsLoaded.accept(groupDatabase.getPushGroupNamesContainingMember(recipientId));
|
|
});
|
|
}
|
|
|
|
void getMemberCount(@NonNull RecipientId recipientId, @NonNull Consumer<GroupMemberCount> onMemberCountLoaded) {
|
|
executor.execute(() -> {
|
|
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
|
Optional<GroupDatabase.GroupRecord> groupRecord = groupDatabase.getGroup(recipientId);
|
|
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);
|
|
}
|
|
}).or(GroupMemberCount.ZERO));
|
|
});
|
|
}
|
|
|
|
void getMessageRequestState(@NonNull Recipient recipient, long threadId, @NonNull Consumer<MessageRequestState> state) {
|
|
executor.execute(() -> state.accept(findMessageRequestState(recipient, threadId)));
|
|
}
|
|
|
|
@WorkerThread
|
|
private MessageRequestState findMessageRequestState(@NonNull Recipient recipient, long threadId) {
|
|
if (!RecipientUtil.isMessageRequestAccepted(context, threadId)) {
|
|
if (recipient.isGroup()) {
|
|
GroupDatabase.MemberLevel memberLevel = DatabaseFactory.getGroupDatabase(context)
|
|
.getGroup(recipient.getId())
|
|
.transform(g -> g.memberLevel(Recipient.self()))
|
|
.or(GroupDatabase.MemberLevel.NOT_A_MEMBER);
|
|
|
|
if (memberLevel == GroupDatabase.MemberLevel.NOT_A_MEMBER) {
|
|
return MessageRequestState.NOT_REQUIRED;
|
|
}
|
|
}
|
|
|
|
return MessageRequestState.REQUIRED;
|
|
} else if (RecipientUtil.isPreMessageRequestThread(context, threadId) && !RecipientUtil.isLegacyProfileSharingAccepted(recipient)) {
|
|
return MessageRequestState.LEGACY;
|
|
} else {
|
|
return MessageRequestState.NOT_REQUIRED;
|
|
}
|
|
}
|
|
|
|
void acceptMessageRequest(@NonNull LiveRecipient liveRecipient,
|
|
long threadId,
|
|
@NonNull Runnable onMessageRequestAccepted,
|
|
@NonNull GroupChangeErrorCallback error)
|
|
{
|
|
executor.execute(()-> {
|
|
if (liveRecipient.get().isPushV2Group()) {
|
|
try {
|
|
Log.i(TAG, "GV2 accepting invite");
|
|
GroupManager.acceptInvite(context, liveRecipient.get().requireGroupId().requireV2());
|
|
|
|
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
|
recipientDatabase.setProfileSharing(liveRecipient.getId(), true);
|
|
|
|
onMessageRequestAccepted.run();
|
|
} catch (GroupChangeException | IOException e) {
|
|
Log.w(TAG, e);
|
|
error.onError(GroupChangeFailureReason.fromException(e));
|
|
}
|
|
} else {
|
|
RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context);
|
|
recipientDatabase.setProfileSharing(liveRecipient.getId(), true);
|
|
|
|
MessageSender.sendProfileKey(context, threadId);
|
|
|
|
List<MessageDatabase.MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(context)
|
|
.setEntireThreadRead(threadId);
|
|
ApplicationDependencies.getMessageNotifier().updateNotification(context);
|
|
MarkReadReceiver.process(context, messageIds);
|
|
|
|
if (TextSecurePreferences.isMultiDevice(context)) {
|
|
ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forAccept(liveRecipient.getId()));
|
|
}
|
|
|
|
onMessageRequestAccepted.run();
|
|
}
|
|
});
|
|
}
|
|
|
|
void deleteMessageRequest(@NonNull LiveRecipient recipient,
|
|
long threadId,
|
|
@NonNull Runnable onMessageRequestDeleted,
|
|
@NonNull GroupChangeErrorCallback error)
|
|
{
|
|
executor.execute(() -> {
|
|
Recipient resolved = recipient.resolve();
|
|
|
|
if (resolved.isGroup() && resolved.requireGroupId().isPush()) {
|
|
try {
|
|
GroupManager.leaveGroupFromBlockOrMessageRequest(context, resolved.requireGroupId().requirePush());
|
|
} catch (GroupChangeException | IOException e) {
|
|
Log.w(TAG, e);
|
|
error.onError(GroupChangeFailureReason.fromException(e));
|
|
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,
|
|
@NonNull GroupChangeErrorCallback error)
|
|
{
|
|
executor.execute(() -> {
|
|
Recipient recipient = liveRecipient.resolve();
|
|
try {
|
|
RecipientUtil.block(context, recipient);
|
|
} catch (GroupChangeException | IOException e) {
|
|
Log.w(TAG, e);
|
|
error.onError(GroupChangeFailureReason.fromException(e));
|
|
return;
|
|
}
|
|
liveRecipient.refresh();
|
|
|
|
if (TextSecurePreferences.isMultiDevice(context)) {
|
|
ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forBlock(liveRecipient.getId()));
|
|
}
|
|
|
|
onMessageRequestBlocked.run();
|
|
});
|
|
}
|
|
|
|
void blockAndDeleteMessageRequest(@NonNull LiveRecipient liveRecipient,
|
|
long threadId,
|
|
@NonNull Runnable onMessageRequestBlocked,
|
|
@NonNull GroupChangeErrorCallback error)
|
|
{
|
|
executor.execute(() -> {
|
|
Recipient recipient = liveRecipient.resolve();
|
|
try{
|
|
RecipientUtil.block(context, recipient);
|
|
} catch (GroupChangeException | IOException e) {
|
|
Log.w(TAG, e);
|
|
error.onError(GroupChangeFailureReason.fromException(e));
|
|
return;
|
|
}
|
|
liveRecipient.refresh();
|
|
|
|
DatabaseFactory.getThreadDatabase(context).deleteConversation(threadId);
|
|
|
|
if (TextSecurePreferences.isMultiDevice(context)) {
|
|
ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forBlockAndDelete(liveRecipient.getId()));
|
|
}
|
|
|
|
onMessageRequestBlocked.run();
|
|
});
|
|
}
|
|
|
|
void unblockAndAccept(@NonNull LiveRecipient liveRecipient, long threadId, @NonNull Runnable onMessageRequestUnblocked) {
|
|
executor.execute(() -> {
|
|
Recipient recipient = liveRecipient.resolve();
|
|
|
|
RecipientUtil.unblock(context, recipient);
|
|
|
|
List<MessageDatabase.MarkedMessageInfo> messageIds = DatabaseFactory.getThreadDatabase(context)
|
|
.setEntireThreadRead(threadId);
|
|
ApplicationDependencies.getMessageNotifier().updateNotification(context);
|
|
MarkReadReceiver.process(context, messageIds);
|
|
|
|
if (TextSecurePreferences.isMultiDevice(context)) {
|
|
ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forAccept(liveRecipient.getId()));
|
|
}
|
|
|
|
onMessageRequestUnblocked.run();
|
|
});
|
|
}
|
|
|
|
boolean isPendingMember(@NonNull GroupId.V2 groupId) {
|
|
return DatabaseFactory.getGroupDatabase(context).isPendingMember(groupId, Recipient.self());
|
|
}
|
|
|
|
enum MessageRequestState {
|
|
/**
|
|
* Message request permission does not need to be gained at this time.
|
|
* <p>
|
|
* Either:
|
|
* - Explicit message request has been accepted, or;
|
|
* - Did not need to be shown because they are a contact etc, or;
|
|
* - It's a group that they are no longer in or invited to.
|
|
*/
|
|
NOT_REQUIRED,
|
|
|
|
/** Explicit message request permission is required. */
|
|
REQUIRED,
|
|
|
|
LEGACY
|
|
}
|
|
}
|