mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-26 03:40:56 +01:00
Implement additional message request improvements.
This commit is contained in:
@@ -230,6 +230,19 @@ public class Recipient {
|
||||
return Recipient.resolved(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* A version of {@link #external(Context, String)} that should be used when you know the
|
||||
* identifier is a groupId.
|
||||
*/
|
||||
@WorkerThread
|
||||
public static @NonNull Recipient externalGroup(@NonNull Context context, @NonNull String groupId) {
|
||||
if (!GroupUtil.isEncodedGroup(groupId)) {
|
||||
throw new IllegalArgumentException("Invalid groupId!");
|
||||
}
|
||||
|
||||
return Recipient.resolved(DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a fully-populated {@link Recipient} based off of a string identifier, creating one in
|
||||
* the database if necessary. The identifier may be a uuid, phone number, email,
|
||||
@@ -706,7 +719,11 @@ public class Recipient {
|
||||
return contactUri != null;
|
||||
}
|
||||
|
||||
public Recipient resolve() {
|
||||
/**
|
||||
* If this recipient is missing crucial data, this will return a populated copy. Otherwise it
|
||||
* returns itself.
|
||||
*/
|
||||
public @NonNull Recipient resolve() {
|
||||
if (resolving) {
|
||||
return live().resolve();
|
||||
} else {
|
||||
@@ -718,6 +735,13 @@ public class Recipient {
|
||||
return resolving;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces retrieving a fresh copy of the recipient, regardless of its state.
|
||||
*/
|
||||
public @NonNull Recipient fresh() {
|
||||
return live().resolve();
|
||||
}
|
||||
|
||||
public @NonNull LiveRecipient live() {
|
||||
return ApplicationDependencies.getRecipientCache().getLive(id);
|
||||
}
|
||||
|
||||
@@ -11,18 +11,18 @@ import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob;
|
||||
import org.thoughtcrime.securesms.jobs.LeaveGroupJob;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceMessageRequestResponseJob;
|
||||
import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||
@@ -76,25 +76,13 @@ public class RecipientUtil {
|
||||
|
||||
DatabaseFactory.getRecipientDatabase(context).setBlocked(resolved.getId(), true);
|
||||
|
||||
if (resolved.isGroup() && DatabaseFactory.getGroupDatabase(context).isActive(resolved.requireGroupId())) {
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(resolved);
|
||||
Optional<OutgoingGroupMediaMessage> leaveMessage = GroupUtil.createGroupLeaveMessage(context, resolved);
|
||||
|
||||
if (threadId != -1 && leaveMessage.isPresent()) {
|
||||
MessageSender.send(context, leaveMessage.get(), threadId, false, null);
|
||||
|
||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||
String groupId = resolved.requireGroupId();
|
||||
groupDatabase.setActive(groupId, false);
|
||||
groupDatabase.remove(groupId, Recipient.self().getId());
|
||||
} else {
|
||||
Log.w(TAG, "Failed to leave group. Can't block.");
|
||||
Toast.makeText(context, R.string.RecipientPreferenceActivity_error_leaving_group, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
if (resolved.isGroup()) {
|
||||
leaveGroup(context, recipient);
|
||||
}
|
||||
|
||||
if (resolved.isSystemContact() || resolved.isProfileSharing()) {
|
||||
ApplicationDependencies.getJobManager().add(new RotateProfileKeyJob());
|
||||
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(resolved.getId(), false);
|
||||
}
|
||||
|
||||
ApplicationDependencies.getJobManager().add(new MultiDeviceBlockedUpdateJob());
|
||||
@@ -108,43 +96,87 @@ public class RecipientUtil {
|
||||
|
||||
DatabaseFactory.getRecipientDatabase(context).setBlocked(recipient.getId(), false);
|
||||
ApplicationDependencies.getJobManager().add(new MultiDeviceBlockedUpdateJob());
|
||||
|
||||
if (FeatureFlags.messageRequests()) {
|
||||
ApplicationDependencies.getJobManager().add(MultiDeviceMessageRequestResponseJob.forAccept(recipient.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public static boolean isThreadMessageRequestAccepted(@NonNull Context context, long threadId) {
|
||||
public static void leaveGroup(@NonNull Context context, @NonNull Recipient recipient) {
|
||||
Recipient resolved = recipient.resolve();
|
||||
|
||||
if (!resolved.isGroup()) {
|
||||
throw new AssertionError("Not a group!");
|
||||
}
|
||||
|
||||
if (DatabaseFactory.getGroupDatabase(context).isActive(resolved.requireGroupId())) {
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(resolved);
|
||||
Optional<OutgoingGroupMediaMessage> leaveMessage = GroupUtil.createGroupLeaveMessage(context, resolved);
|
||||
|
||||
if (threadId != -1 && leaveMessage.isPresent()) {
|
||||
ApplicationDependencies.getJobManager().add(LeaveGroupJob.create(recipient));
|
||||
|
||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||
String groupId = resolved.requireGroupId();
|
||||
groupDatabase.setActive(groupId, false);
|
||||
groupDatabase.remove(groupId, Recipient.self().getId());
|
||||
} else {
|
||||
Log.w(TAG, "Failed to leave group.");
|
||||
Toast.makeText(context, R.string.RecipientPreferenceActivity_error_leaving_group, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
} else {
|
||||
Log.i(TAG, "Group was already inactive. Skipping.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, the new message request UI does not need to be shown, and it's safe to send read
|
||||
* receipts.
|
||||
*
|
||||
* Note that this does not imply that a user has explicitly accepted a message request -- it could
|
||||
* also be the case that the thread in question is for a system contact or something of the like.
|
||||
*/
|
||||
@WorkerThread
|
||||
public static boolean isMessageRequestAccepted(@NonNull Context context, long threadId) {
|
||||
if (!FeatureFlags.messageRequests() || threadId < 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||
Recipient threadRecipient = threadDatabase.getRecipientForThreadId(threadId);
|
||||
|
||||
if (threadRecipient == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isMessageRequestAccepted(context, threadId, threadRecipient);
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link #isMessageRequestAccepted(Context, long)}.
|
||||
*/
|
||||
@WorkerThread
|
||||
public static boolean isMessageRequestAccepted(@NonNull Context context, @Nullable Recipient threadRecipient) {
|
||||
if (!FeatureFlags.messageRequests() || threadRecipient == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(threadRecipient);
|
||||
return isMessageRequestAccepted(context, threadId, threadRecipient);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if a conversation existed before we enabled message requests, otherwise false.
|
||||
*/
|
||||
@WorkerThread
|
||||
public static boolean isPreMessageRequestThread(@NonNull Context context, long threadId) {
|
||||
if (!FeatureFlags.messageRequests()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||
Recipient recipient = threadDatabase.getRecipientForThreadId(threadId);
|
||||
boolean hasSentSecureMessage = DatabaseFactory.getMmsSmsDatabase(context)
|
||||
.getOutgoingSecureConversationCount(threadId) != 0;
|
||||
boolean noSecureMessagesInThread = DatabaseFactory.getMmsSmsDatabase(context)
|
||||
.getSecureConversationCount(threadId) == 0;
|
||||
|
||||
if (recipient == null || hasSentSecureMessage || noSecureMessagesInThread) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Recipient resolved = recipient.resolve();
|
||||
|
||||
return resolved.isProfileSharing() || resolved.isSystemContact();
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public static boolean isRecipientMessageRequestAccepted(@NonNull Context context, @Nullable Recipient recipient) {
|
||||
if (recipient == null || !FeatureFlags.messageRequests()) return true;
|
||||
|
||||
Recipient resolved = recipient.resolve();
|
||||
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(resolved);
|
||||
boolean hasSentMessage = DatabaseFactory.getMmsSmsDatabase(context)
|
||||
.getOutgoingSecureConversationCount(threadId) != 0;
|
||||
boolean noSecureMessagesInThread = DatabaseFactory.getMmsSmsDatabase(context)
|
||||
.getSecureConversationCount(threadId) == 0;
|
||||
|
||||
return noSecureMessagesInThread || hasSentMessage || resolved.isProfileSharing() || resolved.isSystemContact();
|
||||
long beforeTime = SignalStore.getMessageRequestEnableTime();
|
||||
return DatabaseFactory.getMmsSmsDatabase(context).getConversationCount(threadId, beforeTime) > 0;
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
@@ -153,12 +185,45 @@ public class RecipientUtil {
|
||||
return;
|
||||
}
|
||||
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(recipient);
|
||||
boolean firstMessage = DatabaseFactory.getMmsSmsDatabase(context)
|
||||
.getOutgoingSecureConversationCount(threadId) == 0;
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(recipient);
|
||||
|
||||
if (isPreMessageRequestThread(context, threadId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean firstMessage = DatabaseFactory.getMmsSmsDatabase(context).getOutgoingSecureConversationCount(threadId) == 0;
|
||||
|
||||
if (firstMessage) {
|
||||
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient.getId(), true);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isLegacyProfileSharingAccepted(@NonNull Recipient threadRecipient) {
|
||||
return threadRecipient.isLocalNumber() ||
|
||||
threadRecipient.isProfileSharing() ||
|
||||
threadRecipient.isSystemContact() ||
|
||||
!threadRecipient.isRegistered();
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private static boolean isMessageRequestAccepted(@NonNull Context context, long threadId, @NonNull Recipient threadRecipient) {
|
||||
return threadRecipient.isLocalNumber() ||
|
||||
threadRecipient.isProfileSharing() ||
|
||||
threadRecipient.isSystemContact() ||
|
||||
threadRecipient.isForceSmsSelection() ||
|
||||
!threadRecipient.isRegistered() ||
|
||||
hasSentMessageInThread(context, threadId) ||
|
||||
noSecureMessagesInThread(context, threadId) ||
|
||||
isPreMessageRequestThread(context, threadId);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private static boolean hasSentMessageInThread(@NonNull Context context, long threadId) {
|
||||
return DatabaseFactory.getMmsSmsDatabase(context).getOutgoingSecureConversationCount(threadId) != 0;
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private static boolean noSecureMessagesInThread(@NonNull Context context, long threadId) {
|
||||
return DatabaseFactory.getMmsSmsDatabase(context).getSecureConversationCount(threadId) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user