Add missing friend request protocol logic

This commit is contained in:
nielsandriesse 2020-05-14 11:19:20 +10:00
parent 42346cf54b
commit 3c328b3c84
4 changed files with 109 additions and 20 deletions

View File

@ -139,7 +139,6 @@ import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types; import org.thoughtcrime.securesms.database.MmsSmsColumns.Types;
import org.thoughtcrime.securesms.database.RecipientDatabase; import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState; import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.identity.IdentityRecordList; import org.thoughtcrime.securesms.database.identity.IdentityRecordList;
import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MessageRecord;
@ -155,13 +154,13 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil; import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel; import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
import org.thoughtcrime.securesms.loki.activities.HomeActivity; import org.thoughtcrime.securesms.loki.activities.HomeActivity;
import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase; import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabaseDelegate; import org.thoughtcrime.securesms.loki.database.LokiThreadDatabaseDelegate;
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase; import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
import org.thoughtcrime.securesms.loki.protocol.FriendRequestProtocol; import org.thoughtcrime.securesms.loki.protocol.FriendRequestProtocol;
import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol;
import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities; import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities;
import org.thoughtcrime.securesms.loki.views.FriendRequestViewDelegate; import org.thoughtcrime.securesms.loki.views.FriendRequestViewDelegate;
import org.thoughtcrime.securesms.loki.views.MentionCandidateSelectionView; import org.thoughtcrime.securesms.loki.views.MentionCandidateSelectionView;
@ -215,7 +214,6 @@ import org.thoughtcrime.securesms.util.Dialogs;
import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme; import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.ExpirationUtil; import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.IdentityUtil; import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.ServiceUtil;
@ -232,9 +230,7 @@ import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat; import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat;
import org.whispersystems.signalservice.loki.protocol.mentions.Mention; import org.whispersystems.signalservice.loki.protocol.mentions.Mention;
import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager; import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager;
import org.whispersystems.signalservice.loki.protocol.multidevice.DeviceLink;
import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol; import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol;
import org.whispersystems.signalservice.loki.protocol.todo.LokiMessageFriendRequestStatus;
import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus; import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus;
import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation; import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation;
@ -243,7 +239,6 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -456,7 +451,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}); });
sessionRestoreBannerView.setOnRestore(() -> { sessionRestoreBannerView.setOnRestore(() -> {
this.restoreSession(); SessionManagementProtocol.startSessionReset(this, recipient, threadId);
updateSessionRestoreBanner();
return Unit.INSTANCE; return Unit.INSTANCE;
}); });
sessionRestoreBannerView.setOnDismiss(() -> { sessionRestoreBannerView.setOnDismiss(() -> {
@ -1153,7 +1149,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
builder.setMessage(getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group)); builder.setMessage(getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group));
builder.setPositiveButton(R.string.yes, (dialog, which) -> { builder.setPositiveButton(R.string.yes, (dialog, which) -> {
Recipient groupRecipient = getRecipient(); Recipient groupRecipient = getRecipient();
if (GroupUtil.leaveGroup(this, groupRecipient)) { if (ClosedGroupsProtocol.leaveGroup(this, groupRecipient)) {
initializeEnabledCheck(); initializeEnabledCheck();
} else { } else {
Toast.makeText(this, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show();
@ -2276,7 +2272,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
private void updateInputPanel() { private void updateInputPanel() {
boolean shouldInputPanelBeEnabled = FriendRequestProtocol.shouldInputPanelBeEnabled(threadId); boolean shouldInputPanelBeEnabled = FriendRequestProtocol.shouldInputPanelBeEnabled(this, threadId);
Util.runOnMain(() -> { Util.runOnMain(() -> {
updateToggleButtonState(); updateToggleButtonState();
String hint = shouldInputPanelBeEnabled ? "Message" : "Pending session request"; String hint = shouldInputPanelBeEnabled ? "Message" : "Pending session request";
@ -2476,7 +2472,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} }
private void updateToggleButtonState() { private void updateToggleButtonState() {
if (!FriendRequestProtocol.shouldAttachmentsButtonBeEnabled(threadId)) { if (!FriendRequestProtocol.shouldAttachmentButtonBeEnabled(this, threadId)) {
buttonToggle.display(sendButton); buttonToggle.display(sendButton);
quickAttachmentToggle.hide(); quickAttachmentToggle.hide();
inlineAttachmentToggle.hide(); inlineAttachmentToggle.hide();
@ -2872,7 +2868,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
LokiThreadDatabase threadDatabase = DatabaseFactory.getLokiThreadDatabase(ConversationActivity.this); LokiThreadDatabase threadDatabase = DatabaseFactory.getLokiThreadDatabase(ConversationActivity.this);
LokiUserDatabase userDatabase = DatabaseFactory.getLokiUserDatabase(ConversationActivity.this); LokiUserDatabase userDatabase = DatabaseFactory.getLokiUserDatabase(ConversationActivity.this);
if (lastCharacter == '@' && Character.isWhitespace(secondToLastCharacter)) { if (lastCharacter == '@' && Character.isWhitespace(secondToLastCharacter)) {
List<Mention> mentionCandidates = MentionsManager.INSTANCE.getMentionCandidates("", threadId, userHexEncodedPublicKey, threadDatabase, userDatabase); List<Mention> mentionCandidates = MentionsManager.shared.getMentionCandidates("", threadId);
currentMentionStartIndex = lastCharacterIndex; currentMentionStartIndex = lastCharacterIndex;
mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE); mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE);
mentionCandidateSelectionView.show(mentionCandidates, threadId); mentionCandidateSelectionView.show(mentionCandidates, threadId);
@ -2883,7 +2879,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} else { } else {
if (currentMentionStartIndex != -1) { if (currentMentionStartIndex != -1) {
String query = text.substring(currentMentionStartIndex + 1); // + 1 to get rid of the @ String query = text.substring(currentMentionStartIndex + 1); // + 1 to get rid of the @
List<Mention> mentionCandidates = MentionsManager.INSTANCE.getMentionCandidates(query, threadId, userHexEncodedPublicKey, threadDatabase, userDatabase); List<Mention> mentionCandidates = MentionsManager.shared.getMentionCandidates(query, threadId);
mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE); mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE);
mentionCandidateSelectionView.show(mentionCandidates, threadId); mentionCandidateSelectionView.show(mentionCandidates, threadId);
} }
@ -3190,5 +3186,20 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
updateSubtitleTextView(); updateSubtitleTextView();
updateMessageStatusProgressBar(); updateMessageStatusProgressBar();
} }
@Override
public void acceptFriendRequest(@NotNull MessageRecord friendRequest) {
if (recipient.isGroupRecipient()) { return; }
FriendRequestProtocol.acceptFriendRequest(this, recipient);
updateInputPanel();
}
@Override
public void rejectFriendRequest(@NotNull MessageRecord friendRequest) {
if (recipient.isGroupRecipient()) { return; }
FriendRequestProtocol.rejectFriendRequest(this, recipient);
updateInputPanel();
}
// endregion // endregion
} }

View File

@ -6,6 +6,7 @@ import org.thoughtcrime.securesms.database.Address
import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.loki.utilities.recipient import org.thoughtcrime.securesms.loki.utilities.recipient
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage import org.thoughtcrime.securesms.mms.OutgoingMediaMessage
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.sms.OutgoingTextMessage import org.thoughtcrime.securesms.sms.OutgoingTextMessage
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.api.messages.SignalServiceContent import org.whispersystems.signalservice.api.messages.SignalServiceContent
@ -16,12 +17,72 @@ import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendReque
object FriendRequestProtocol { object FriendRequestProtocol {
@JvmStatic @JvmStatic
fun acceptFriendRequest(context: Context, contactPublicKey: String) { fun acceptFriendRequest(context: Context, recipient: Recipient) {
if (recipient.isGroupRecipient) { return; }
val userPublicKey = TextSecurePreferences.getLocalNumber(context)
val allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey)
// Accept all outstanding friend requests associated with this user and try to establish sessions with the
// subset of their devices that haven't sent a friend request.
val linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(recipient.address.serialize())
val threadDB = DatabaseFactory.getThreadDatabase(context)
val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context)
for (device in linkedDevices) {
val deviceAsRecipient = recipient(context, device)
val deviceThreadID = threadDB.getThreadIdFor(deviceAsRecipient)
val deviceFRStatus = lokiThreadDB.getFriendRequestStatus(deviceThreadID)
if (deviceFRStatus == LokiThreadFriendRequestStatus.REQUEST_RECEIVED) {
lokiThreadDB.setFriendRequestStatus(deviceThreadID, LokiThreadFriendRequestStatus.FRIENDS)
val lastMessageID = getLastMessageID(context, deviceThreadID)
if (lastMessageID != null) {
DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(lastMessageID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED)
}
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient(context, device), true)
val ephemeralMessage = EphemeralMessage.create(device)
ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(ephemeralMessage))
// Sync contact if needed
if (allUserDevices.contains(device)) { return }
val deviceToSync = MultiDeviceProtocol.shared.getMasterDevice(device) ?: device
SyncMessagesProtocol.syncContact(context, Address.fromSerialized(deviceToSync))
} else if (deviceFRStatus == LokiThreadFriendRequestStatus.REQUEST_SENT) {
// Do nothing
} else if (!allUserDevices.contains(device)
&& (deviceFRStatus == LokiThreadFriendRequestStatus.NONE || deviceFRStatus == LokiThreadFriendRequestStatus.REQUEST_EXPIRED)) {
// TODO: Send AFR to contact (NOT their linked devices)
}
}
}
@JvmStatic
fun rejectFriendRequest(context: Context, recipient: Recipient) {
if (recipient.isGroupRecipient) { return; }
val linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(recipient.address.serialize())
val threadDB = DatabaseFactory.getThreadDatabase(context)
val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context)
for (device in linkedDevices) {
val deviceAsRecipient = recipient(context, device)
val deviceThreadID = threadDB.getThreadIdFor(deviceAsRecipient)
val deviceFRStatus = lokiThreadDB.getFriendRequestStatus(deviceThreadID)
// We only want to decline incoming requests
if (deviceFRStatus == LokiThreadFriendRequestStatus.REQUEST_RECEIVED) {
// Delete the pre key bundle for the given contact. This ensures that if we send a
// new message after this, it restarts the friend request process from scratch.
DatabaseFactory.getLokiPreKeyBundleDatabase(context).removePreKeyBundle(device)
lokiThreadDB.setFriendRequestStatus(deviceThreadID, LokiThreadFriendRequestStatus.NONE)
val lastMessageID = getLastMessageID(context, deviceThreadID)
if (lastMessageID != null) {
DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(lastMessageID, LokiMessageFriendRequestStatus.REQUEST_REJECTED)
}
}
}
}
@JvmStatic
fun shouldInputPanelBeEnabled(context: Context, threadID: Long): Boolean {
} }
@JvmStatic @JvmStatic
fun rejectFriendRequest(context: Context, contactPublicKey: String) { fun shouldAttachmentButtonBeEnabled(context: Context, threadID: Long): Boolean {
} }
@ -51,6 +112,7 @@ object FriendRequestProtocol {
if (lastMessageID != null) { if (lastMessageID != null) {
DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(lastMessageID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED) DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(lastMessageID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED)
} }
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true)
// Send a contact sync message if needed // Send a contact sync message if needed
val userPublicKey = TextSecurePreferences.getLocalNumber(context) val userPublicKey = TextSecurePreferences.getLocalNumber(context)
val allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey) val allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey)
@ -107,6 +169,7 @@ object FriendRequestProtocol {
if (lastMessageID != null) { if (lastMessageID != null) {
DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(lastMessageID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED) DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(lastMessageID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED)
} }
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true)
val ephemeralMessage = EphemeralMessage.create(publicKey) val ephemeralMessage = EphemeralMessage.create(publicKey)
ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(ephemeralMessage)) ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(ephemeralMessage))
} else if (threadFRStatus != LokiThreadFriendRequestStatus.FRIENDS) { } else if (threadFRStatus != LokiThreadFriendRequestStatus.FRIENDS) {

View File

@ -10,17 +10,31 @@ import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore
import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.jobs.CleanPreKeysJob import org.thoughtcrime.securesms.jobs.CleanPreKeysJob
import org.thoughtcrime.securesms.loki.utilities.recipient import org.thoughtcrime.securesms.loki.utilities.recipient
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.sms.OutgoingTextMessage
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.libsignal.loki.LokiSessionResetStatus import org.whispersystems.libsignal.loki.LokiSessionResetStatus
import org.whispersystems.signalservice.api.messages.SignalServiceContent import org.whispersystems.signalservice.api.messages.SignalServiceContent
import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus
object SessionManagementProtocol { object SessionManagementProtocol {
@JvmStatic @JvmStatic
fun startSessionReset(context: Context, contactPublicKey: String) { fun startSessionReset(context: Context, recipient: Recipient, threadID: Long) {
if (recipient.isGroupRecipient) { return }
val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context)
val smsDB = DatabaseFactory.getSmsDatabase(context)
val devices = lokiThreadDB.getSessionRestoreDevices(threadID)
for (device in devices) {
val sessionRestorationRequest = EphemeralMessage.createSessionRestorationRequest(recipient.address.serialize())
ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(sessionRestorationRequest))
}
val infoMessage = OutgoingTextMessage(recipient, "", 0, 0)
val infoMessageID = smsDB.insertMessageOutbox(threadID, infoMessage, false, System.currentTimeMillis(), null)
if (infoMessageID > -1) {
smsDB.markAsSentLokiSessionRestorationRequest(infoMessageID)
}
lokiThreadDB.removeAllSessionRestoreDevices(threadID)
} }
@JvmStatic @JvmStatic

View File

@ -104,15 +104,16 @@ object SyncMessagesProtocol {
val threadFRStatus = lokiThreadDB.getFriendRequestStatus(threadID) val threadFRStatus = lokiThreadDB.getFriendRequestStatus(threadID)
when (threadFRStatus) { when (threadFRStatus) {
LokiThreadFriendRequestStatus.NONE, LokiThreadFriendRequestStatus.REQUEST_EXPIRED -> { LokiThreadFriendRequestStatus.NONE, LokiThreadFriendRequestStatus.REQUEST_EXPIRED -> {
// TODO: Send AFR to contact and their linked devices // TODO: Send AFR to contact AND THEIR LINKED DEVICES
} }
LokiThreadFriendRequestStatus.REQUEST_RECEIVED -> { LokiThreadFriendRequestStatus.REQUEST_RECEIVED -> {
FriendRequestProtocol.acceptFriendRequest(contactPublicKey) // Takes into account multi device internally FriendRequestProtocol.acceptFriendRequest(context, recipient(context, contactPublicKey)) // Takes into account multi device internally
lokiThreadDB.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.FRIENDS) lokiThreadDB.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.FRIENDS)
val lastMessageID = FriendRequestProtocol.getLastMessageID(context, threadID) val lastMessageID = FriendRequestProtocol.getLastMessageID(context, threadID)
if (lastMessageID != null) { if (lastMessageID != null) {
DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(lastMessageID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED) DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(lastMessageID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED)
} }
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient(context, contactPublicKey), true)
} }
else -> { else -> {
// Do nothing // Do nothing