From d0742cf09f22b0758fa3510f18380fc5d7df43de Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Wed, 13 May 2020 11:15:17 +1000 Subject: [PATCH] Refactor multi device message sending --- .../securesms/ConversationListActivity.java | 4 +- .../loki/RecipientAvatarModifiedEvent.kt | 5 - .../loki/protocol/EphemeralMessage.kt | 2 +- .../protocol/MultiDeviceOpenGroupUpdateJob.kt | 1 + .../loki/protocol/MultiDeviceProtocol.kt | 130 ++++++++++-------- .../protocol/PushEphemeralMessageSendJob.kt | 8 +- .../loki/protocol/SyncMessagesProtocol.kt | 1 + .../utilities/ProfilePictureModifiedEvent.kt | 5 + .../securesms/recipients/Recipient.java | 4 +- 9 files changed, 83 insertions(+), 77 deletions(-) delete mode 100644 src/org/thoughtcrime/securesms/loki/RecipientAvatarModifiedEvent.kt create mode 100644 src/org/thoughtcrime/securesms/loki/utilities/ProfilePictureModifiedEvent.kt diff --git a/src/org/thoughtcrime/securesms/ConversationListActivity.java b/src/org/thoughtcrime/securesms/ConversationListActivity.java index 0f717f4787..1c3180ee35 100644 --- a/src/org/thoughtcrime/securesms/ConversationListActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationListActivity.java @@ -49,7 +49,7 @@ import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo; import org.thoughtcrime.securesms.lock.RegistrationLockDialog; -import org.thoughtcrime.securesms.loki.RecipientAvatarModifiedEvent; +import org.thoughtcrime.securesms.loki.utilities.ProfilePictureModifiedEvent; import org.thoughtcrime.securesms.loki.activities.JoinPublicChatActivity; import org.thoughtcrime.securesms.mms.GlideApp; import org.thoughtcrime.securesms.notifications.MarkReadReceiver; @@ -330,7 +330,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit } @Subscribe(threadMode = ThreadMode.MAIN) - public void onAvatarModified(RecipientAvatarModifiedEvent event) { + public void onAvatarModified(ProfilePictureModifiedEvent event) { Recipient recipient = event.getRecipient(); if (recipient.isLocalNumber() || recipient.isUserMasterDevice()) { initializeProfileIcon(recipient); diff --git a/src/org/thoughtcrime/securesms/loki/RecipientAvatarModifiedEvent.kt b/src/org/thoughtcrime/securesms/loki/RecipientAvatarModifiedEvent.kt deleted file mode 100644 index 4faf84714b..0000000000 --- a/src/org/thoughtcrime/securesms/loki/RecipientAvatarModifiedEvent.kt +++ /dev/null @@ -1,5 +0,0 @@ -package org.thoughtcrime.securesms.loki - -import org.thoughtcrime.securesms.recipients.Recipient - -data class RecipientAvatarModifiedEvent(val recipient: Recipient) \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/loki/protocol/EphemeralMessage.kt b/src/org/thoughtcrime/securesms/loki/protocol/EphemeralMessage.kt index e9c8377101..c813dc813a 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/EphemeralMessage.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/EphemeralMessage.kt @@ -2,7 +2,7 @@ package org.thoughtcrime.securesms.loki.protocol import org.whispersystems.signalservice.internal.util.JsonUtil -data class EphemeralMessage private constructor(val data: Map<*, *>) { +class EphemeralMessage private constructor(val data: Map<*, *>) { companion object { diff --git a/src/org/thoughtcrime/securesms/loki/protocol/MultiDeviceOpenGroupUpdateJob.kt b/src/org/thoughtcrime/securesms/loki/protocol/MultiDeviceOpenGroupUpdateJob.kt index 2c6ed90ff0..1b60841643 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/MultiDeviceOpenGroupUpdateJob.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/MultiDeviceOpenGroupUpdateJob.kt @@ -19,6 +19,7 @@ import javax.inject.Inject class MultiDeviceOpenGroupUpdateJob private constructor(parameters: Parameters) : BaseJob(parameters), InjectableType { companion object { + const val KEY = "MultiDeviceOpenGroupUpdateJob" } diff --git a/src/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt index aa0c7d823f..e77226f99c 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt @@ -2,7 +2,17 @@ package org.thoughtcrime.securesms.loki.protocol import android.content.Context import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.database.Address +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.jobs.PushMediaSendJob +import org.thoughtcrime.securesms.jobs.PushSendJob +import org.thoughtcrime.securesms.jobs.PushTextSendJob import org.thoughtcrime.securesms.recipients.Recipient +import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI +import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol +import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol +import org.whispersystems.signalservice.loki.protocol.todo.LokiMessageFriendRequestStatus +import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus object MultiDeviceProtocol { @@ -12,6 +22,8 @@ object MultiDeviceProtocol { ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(unlinkingRequest)) } + enum class MessageType { Text, Media } + @JvmStatic fun sendTextPush(context: Context, recipient: Recipient, messageID: Long) { @@ -22,65 +34,61 @@ object MultiDeviceProtocol { } -// private static void sendMessagePush(Context context, MessageType type, Recipient recipient, long messageId) { -// JobManager jobManager = ApplicationContext.getInstance(context).getJobManager(); -// -// // Just send the message normally if it's a group message or we're sending to one of our devices -// String recipientHexEncodedPublicKey = recipient.getAddress().serialize(); -// if (GeneralUtilitiesKt.isPublicChat(context, recipientHexEncodedPublicKey) || PromiseUtil.get(MultiDeviceUtilities.isOneOfOurDevices(context, recipient.getAddress()), false)) { -// if (type == MessageType.MEDIA) { -// PushMediaSendJob.enqueue(context, jobManager, messageId, recipient.getAddress(), false); -// } else { -// jobManager.add(new PushTextSendJob(messageId, recipient.getAddress())); -// } -// return; -// } -// -// // If we get here then we are sending a message to a device that is not ours -// boolean[] hasSentSyncMessage = { false }; -// MultiDeviceUtilities.getAllDevicePublicKeysWithFriendStatus(context, recipientHexEncodedPublicKey).success(devices -> { -// int friendCount = MultiDeviceUtilities.getFriendCount(context, devices.keySet()); -// Util.runOnMain(() -> { -// ArrayList jobs = new ArrayList<>(); -// for (Map.Entry entry : devices.entrySet()) { -// String deviceHexEncodedPublicKey = entry.getKey(); -// boolean isFriend = entry.getValue(); -// -// Address address = Address.fromSerialized(deviceHexEncodedPublicKey); -// long messageIDToUse = recipientHexEncodedPublicKey.equals(deviceHexEncodedPublicKey) ? messageId : -1L; -// -// if (isFriend) { -// // Send a normal message if the user is friends with the recipient -// // We should also send a sync message if we haven't already sent one -// boolean shouldSendSyncMessage = !hasSentSyncMessage[0] && address.isPhone(); -// if (type == MessageType.MEDIA) { -// jobs.add(new PushMediaSendJob(messageId, messageIDToUse, address, false, null, shouldSendSyncMessage)); -// } else { -// jobs.add(new PushTextSendJob(messageId, messageIDToUse, address, shouldSendSyncMessage)); -// } -// if (shouldSendSyncMessage) { hasSentSyncMessage[0] = true; } -// } else { -// // Send friend requests to non-friends. If the user is friends with any -// // of the devices then send out a default friend request message. -// boolean isFriendsWithAny = (friendCount > 0); -// String defaultFriendRequestMessage = isFriendsWithAny ? "Please accept to enable messages to be synced across devices" : null; -// if (type == MessageType.MEDIA) { -// jobs.add(new PushMediaSendJob(messageId, messageIDToUse, address, true, defaultFriendRequestMessage, false)); -// } else { -// jobs.add(new PushTextSendJob(messageId, messageIDToUse, address, true, defaultFriendRequestMessage, false)); -// } -// } -// } -// -// // Start the send -// if (type == MessageType.MEDIA) { -// PushMediaSendJob.enqueue(context, jobManager, (List)(List)jobs); -// } else { -// // Schedule text send jobs -// jobManager.startChain(jobs).enqueue(); -// } -// }); -// return Unit.INSTANCE; -// }); -// } + private fun sendMessagePushToDevice(context: Context, recipient: Recipient, messageID: Long, messageType: MessageType): PushSendJob { + val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient) + val threadFRStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID) + val isContactFriend = (threadFRStatus == LokiThreadFriendRequestStatus.FRIENDS) + val messageFRStatus = DatabaseFactory.getLokiMessageDatabase(context).getFriendRequestStatus(messageID) + val isFRMessage = (messageFRStatus != LokiMessageFriendRequestStatus.NONE) + val hasVisibleContent = when (messageType) { + MessageType.Text -> DatabaseFactory.getSmsDatabase(context).getMessage(messageID).body.isNotBlank() + MessageType.Media -> { + val outgoingMediaMessage = DatabaseFactory.getMmsDatabase(context).getOutgoingMessage(messageID) + outgoingMediaMessage.body.isNotBlank() || outgoingMediaMessage.attachments.isNotEmpty() + } + } + val shouldSendAutoGeneratedFR = !isContactFriend && !isFRMessage + && !SessionMetaProtocol.shared.isNoteToSelf(recipient.address.serialize()) && !recipient.address.isGroup // Group threads work through session requests + && hasVisibleContent + if (!shouldSendAutoGeneratedFR) { + when (messageType) { + MessageType.Text -> return PushTextSendJob(messageID, recipient.address) + MessageType.Media -> return PushMediaSendJob(messageID, recipient.address) + } + } else { + val autoGeneratedFRMessage = "Please accept to enable messages to be synced across devices" + when (messageType) { + MessageType.Text -> return PushTextSendJob(messageID, messageID, recipient.address, true, autoGeneratedFRMessage) + MessageType.Media -> return PushMediaSendJob(messageID, messageID, recipient.address, true, autoGeneratedFRMessage) + } + } + } + + private fun sendMessagePush(context: Context, recipient: Recipient, messageID: Long, messageType: MessageType) { + val jobManager = ApplicationContext.getInstance(context).jobManager + val isMultiDeviceRequired = !recipient.address.isOpenGroup + if (!isMultiDeviceRequired) { + when (messageType) { + MessageType.Text -> jobManager.add(PushTextSendJob(messageID, recipient.address)) + MessageType.Media -> PushMediaSendJob.enqueue(context, jobManager, messageID, recipient.address) + } + } + val publicKey = recipient.address.serialize() + LokiFileServerAPI.shared.getDeviceLinks(publicKey).success { + val devices = MultiDeviceProtocol.shared.getAllLinkedDevices(publicKey) + val jobs = devices.map { sendMessagePushToDevice(context, Recipient.from(context, Address.fromSerialized(it), false), messageID, messageType) } + @Suppress("UNCHECKED_CAST") + when (messageType) { + MessageType.Text -> jobManager.startChain(jobs).enqueue() + MessageType.Media -> PushMediaSendJob.enqueue(context, jobManager, jobs as List) + } + }.fail { exception -> + // Proceed even if updating the recipient's device links failed, so that message sending + // is independent of whether the file server is online + when (messageType) { + MessageType.Text -> jobManager.add(PushTextSendJob(messageID, recipient.address)) + MessageType.Media -> PushMediaSendJob.enqueue(context, jobManager, messageID, recipient.address) + } + } + } } diff --git a/src/org/thoughtcrime/securesms/loki/protocol/PushEphemeralMessageSendJob.kt b/src/org/thoughtcrime/securesms/loki/protocol/PushEphemeralMessageSendJob.kt index 048aa97e8a..74cc3fcf3f 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/PushEphemeralMessageSendJob.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/PushEphemeralMessageSendJob.kt @@ -15,16 +15,12 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress import java.io.IOException import java.util.concurrent.TimeUnit -class PushEphemeralMessageSendJob private constructor( - parameters: Parameters, - private val message: EphemeralMessage -) : BaseJob(parameters) { +class PushEphemeralMessageSendJob private constructor(parameters: Parameters, private val message: EphemeralMessage) : BaseJob(parameters) { companion object { + private val KEY_MESSAGE = "message" const val KEY = "PushBackgroundMessageSendJob" - - private val KEY_MESSAGE = "message" } constructor(message: EphemeralMessage) : this(Parameters.Builder() diff --git a/src/org/thoughtcrime/securesms/loki/protocol/SyncMessagesProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/SyncMessagesProtocol.kt index 5f4a1a88d6..96b09268af 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/SyncMessagesProtocol.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/SyncMessagesProtocol.kt @@ -40,6 +40,7 @@ object SyncMessagesProtocol { fun shouldSyncContact(context: Context, address: Address): Boolean { if (!PublicKeyValidation.isValid(address.serialize())) { return false } if (address.serialize() == TextSecurePreferences.getMasterHexEncodedPublicKey(context)) { return false } + if (address.serialize() == TextSecurePreferences.getLocalNumber(context)) { return false } val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(Recipient.from(context, address, false)) val isFriend = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID) == LokiThreadFriendRequestStatus.FRIENDS return isFriend diff --git a/src/org/thoughtcrime/securesms/loki/utilities/ProfilePictureModifiedEvent.kt b/src/org/thoughtcrime/securesms/loki/utilities/ProfilePictureModifiedEvent.kt new file mode 100644 index 0000000000..a05676ef62 --- /dev/null +++ b/src/org/thoughtcrime/securesms/loki/utilities/ProfilePictureModifiedEvent.kt @@ -0,0 +1,5 @@ +package org.thoughtcrime.securesms.loki.utilities + +import org.thoughtcrime.securesms.recipients.Recipient + +data class ProfilePictureModifiedEvent(val recipient: Recipient) \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/recipients/Recipient.java b/src/org/thoughtcrime/securesms/recipients/Recipient.java index 4cd243114d..297a6cf6b7 100644 --- a/src/org/thoughtcrime/securesms/recipients/Recipient.java +++ b/src/org/thoughtcrime/securesms/recipients/Recipient.java @@ -44,7 +44,7 @@ import org.thoughtcrime.securesms.database.RecipientDatabase.UnidentifiedAccessM import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.loki.todo.JazzIdenticonContactPhoto; -import org.thoughtcrime.securesms.loki.RecipientAvatarModifiedEvent; +import org.thoughtcrime.securesms.loki.utilities.ProfilePictureModifiedEvent; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.recipients.RecipientProvider.RecipientDetails; import org.thoughtcrime.securesms.util.FutureTaskListener; @@ -399,7 +399,7 @@ public class Recipient implements RecipientModifiedListener { } notifyListeners(); - EventBus.getDefault().post(new RecipientAvatarModifiedEvent(this)); + EventBus.getDefault().post(new ProfilePictureModifiedEvent(this)); } public synchronized boolean isProfileSharing() {