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 { @JvmStatic fun sendUnlinkingRequest(context: Context, publicKey: String) { val unlinkingRequest = EphemeralMessage.createUnlinkingRequest(publicKey) ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(unlinkingRequest)) } enum class MessageType { Text, Media } @JvmStatic fun sendTextPush(context: Context, recipient: Recipient, messageID: Long) { } @JvmStatic fun sendMediaPush(context: Context, recipient: Recipient, messageID: Long) { } 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) } } } }