mirror of
https://github.com/oxen-io/session-android.git
synced 2025-02-19 20:58:26 +00:00
Refactor MessageSender
This commit is contained in:
parent
819f414446
commit
eb5b8886d4
@ -14,7 +14,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver;
|
||||
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceOpenGroupUpdateJob;
|
||||
import org.thoughtcrime.securesms.loki.PushBackgroundMessageSendJob;
|
||||
import org.thoughtcrime.securesms.loki.protocol.PushEphemeralMessageSendJob;
|
||||
import org.thoughtcrime.securesms.loki.PushMessageSyncSendJob;
|
||||
|
||||
import java.util.Arrays;
|
||||
@ -72,7 +72,7 @@ public final class JobManagerFactories {
|
||||
put(TypingSendJob.KEY, new TypingSendJob.Factory());
|
||||
put(UpdateApkJob.KEY, new UpdateApkJob.Factory());
|
||||
put(PushMessageSyncSendJob.KEY, new PushMessageSyncSendJob.Factory());
|
||||
put(PushBackgroundMessageSendJob.KEY, new PushBackgroundMessageSendJob.Factory());
|
||||
put(PushEphemeralMessageSendJob.KEY, new PushEphemeralMessageSendJob.Factory());
|
||||
put(MultiDeviceOpenGroupUpdateJob.KEY, new MultiDeviceOpenGroupUpdateJob.Factory());
|
||||
}};
|
||||
}
|
||||
|
@ -1,135 +0,0 @@
|
||||
package org.thoughtcrime.securesms.loki
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
|
||||
import org.thoughtcrime.securesms.database.Address
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.jobmanager.Data
|
||||
import org.thoughtcrime.securesms.jobmanager.Job
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
||||
import org.thoughtcrime.securesms.jobs.BaseJob
|
||||
import org.thoughtcrime.securesms.logging.Log
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
|
||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||
import org.whispersystems.signalservice.internal.util.JsonUtil
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
data class BackgroundMessage private constructor(val data: Map<String, Any>) {
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun create(recipient: String) = BackgroundMessage(mapOf("recipient" to recipient))
|
||||
|
||||
@JvmStatic
|
||||
fun createFriendRequest(recipient: String, messageBody: String) = BackgroundMessage(mapOf( "recipient" to recipient, "body" to messageBody, "friendRequest" to true ))
|
||||
|
||||
@JvmStatic
|
||||
fun createUnpairingRequest(recipient: String) = BackgroundMessage(mapOf( "recipient" to recipient, "unpairingRequest" to true ))
|
||||
|
||||
@JvmStatic
|
||||
fun createSessionRestore(recipient: String) = BackgroundMessage(mapOf( "recipient" to recipient, "friendRequest" to true, "sessionRestore" to true ))
|
||||
|
||||
@JvmStatic
|
||||
fun createSessionRequest(recipient: String) = BackgroundMessage(mapOf("recipient" to recipient, "friendRequest" to true, "sessionRequest" to true))
|
||||
|
||||
internal fun parse(serialized: String): BackgroundMessage {
|
||||
val data = JsonUtil.fromJson(serialized, Map::class.java) as? Map<String, Any> ?: throw AssertionError("JSON parsing failed")
|
||||
return BackgroundMessage(data)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> get(key: String, defaultValue: T): T {
|
||||
return data[key] as? T ?: defaultValue
|
||||
}
|
||||
|
||||
fun serialize(): String {
|
||||
return JsonUtil.toJson(data)
|
||||
}
|
||||
}
|
||||
|
||||
class PushBackgroundMessageSendJob private constructor(
|
||||
parameters: Parameters,
|
||||
private val message: BackgroundMessage
|
||||
) : BaseJob(parameters) {
|
||||
companion object {
|
||||
const val KEY = "PushBackgroundMessageSendJob"
|
||||
|
||||
private val TAG = PushBackgroundMessageSendJob::class.java.simpleName
|
||||
|
||||
private val KEY_MESSAGE = "message"
|
||||
}
|
||||
|
||||
constructor(message: BackgroundMessage) : this(Parameters.Builder()
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setQueue(KEY)
|
||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
||||
.setMaxAttempts(1)
|
||||
.build(),
|
||||
message)
|
||||
|
||||
override fun serialize(): Data {
|
||||
return Data.Builder()
|
||||
.putString(KEY_MESSAGE, message.serialize())
|
||||
.build()
|
||||
}
|
||||
|
||||
override fun getFactoryKey(): String {
|
||||
return KEY
|
||||
}
|
||||
|
||||
public override fun onRun() {
|
||||
val recipient = message.get<String?>("recipient", null) ?: throw IllegalStateException()
|
||||
val dataMessage = SignalServiceDataMessage.newBuilder()
|
||||
.withTimestamp(System.currentTimeMillis())
|
||||
.withBody(message.get<String?>("body", null))
|
||||
|
||||
if (message.get("friendRequest", false)) {
|
||||
val bundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(recipient)
|
||||
dataMessage.withPreKeyBundle(bundle)
|
||||
.asFriendRequest(true)
|
||||
}
|
||||
|
||||
if (message.get("unpairingRequest", false)) {
|
||||
dataMessage.asUnlinkingRequest(true)
|
||||
}
|
||||
|
||||
if (message.get("sessionRestore", false)) {
|
||||
dataMessage.asSessionRestorationRequest(true)
|
||||
}
|
||||
|
||||
if (message.get("sessionRequest", false)) {
|
||||
dataMessage.asSessionRequest(true)
|
||||
}
|
||||
|
||||
val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
|
||||
val address = SignalServiceAddress(recipient)
|
||||
try {
|
||||
val udAccess = UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(recipient), false))
|
||||
messageSender.sendMessage(-1, address, udAccess, dataMessage.build()) // The message ID doesn't matter
|
||||
} catch (e: Exception) {
|
||||
Log.d("Loki", "Failed to send background message to: ${recipient}.")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
public override fun onShouldRetry(e: Exception): Boolean {
|
||||
// Loki - Disable since we have our own retrying when sending messages
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onCanceled() {}
|
||||
|
||||
class Factory : Job.Factory<PushBackgroundMessageSendJob> {
|
||||
override fun create(parameters: Parameters, data: Data): PushBackgroundMessageSendJob {
|
||||
try {
|
||||
val messageJSON = data.getString(KEY_MESSAGE)
|
||||
return PushBackgroundMessageSendJob(parameters, BackgroundMessage.parse(messageJSON))
|
||||
} catch (e: IOException) {
|
||||
throw AssertionError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package org.thoughtcrime.securesms.loki.protocol
|
||||
|
||||
import android.content.Context
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore
|
||||
import org.thoughtcrime.securesms.database.Address
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
@ -79,9 +80,13 @@ object ClosedGroupsProtocol {
|
||||
for (device in allDevices) {
|
||||
val address = SignalProtocolAddress(device, SignalServiceAddress.DEFAULT_DEVICE_ID)
|
||||
val hasSession = TextSecureSessionStore(context).containsSession(address)
|
||||
if (!hasSession) {
|
||||
MessageSender.sendBackgroundSessionRequest(context, device)
|
||||
}
|
||||
if (!hasSession) { sendSessionRequest(context, device) }
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun sendSessionRequest(context: Context, publicKey: String) {
|
||||
val sessionRequest = EphemeralMessage.createSessionRequest(publicKey)
|
||||
ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(sessionRequest))
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package org.thoughtcrime.securesms.loki.protocol
|
||||
|
||||
import org.whispersystems.signalservice.internal.util.JsonUtil
|
||||
|
||||
data class EphemeralMessage private constructor(val data: Map<*, *>) {
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun create(publicKey: String) = EphemeralMessage(mapOf( "recipient" to publicKey ))
|
||||
|
||||
@JvmStatic
|
||||
fun createUnlinkingRequest(publicKey: String) = EphemeralMessage(mapOf( "recipient" to publicKey, "unpairingRequest" to true ))
|
||||
|
||||
@JvmStatic
|
||||
fun createSessionRestorationRequest(publicKey: String) = EphemeralMessage(mapOf( "recipient" to publicKey, "friendRequest" to true, "sessionRestore" to true ))
|
||||
|
||||
@JvmStatic
|
||||
fun createSessionRequest(publicKey: String) = EphemeralMessage(mapOf("recipient" to publicKey, "friendRequest" to true, "sessionRequest" to true))
|
||||
|
||||
internal fun parse(serialized: String): EphemeralMessage {
|
||||
val data = JsonUtil.fromJson(serialized, Map::class.java) ?: throw IllegalArgumentException("Couldn't parse string to JSON")
|
||||
return EphemeralMessage(data)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> get(key: String, defaultValue: T): T {
|
||||
return data[key] as? T ?: defaultValue
|
||||
}
|
||||
|
||||
fun serialize(): String {
|
||||
return JsonUtil.toJson(data)
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package org.thoughtcrime.securesms.loki.protocol
|
||||
|
||||
import android.content.Context
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage
|
||||
import org.thoughtcrime.securesms.sms.OutgoingTextMessage
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.signalservice.loki.protocol.todo.LokiMessageFriendRequestStatus
|
||||
import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus
|
||||
|
||||
object FriendRequestProtocol {
|
||||
|
||||
@JvmStatic
|
||||
fun shouldUpdateFriendRequestStatusFromOutgoingTextMessage(context: Context, message: OutgoingTextMessage): Boolean {
|
||||
// The order of these checks matters
|
||||
if (message.recipient.isGroupRecipient) { return false }
|
||||
if (message.recipient.address.serialize() == TextSecurePreferences.getLocalNumber(context)) { return false }
|
||||
// TODO: Return false if the message is a device linking request
|
||||
// TODO: Return false if the message is a session request
|
||||
return message.isFriendRequest
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun shouldUpdateFriendRequestStatusFromOutgoingMediaMessage(context: Context, message: OutgoingMediaMessage): Boolean {
|
||||
// The order of these checks matters
|
||||
if (message.recipient.isGroupRecipient) { return false }
|
||||
if (message.recipient.address.serialize() == TextSecurePreferences.getLocalNumber(context)) { return false }
|
||||
// TODO: Return false if the message is a device linking request
|
||||
// TODO: Return false if the message is a session request
|
||||
return message.isFriendRequest
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun setFriendRequestStatusToSendingIfNeeded(context: Context, messageID: Long, threadID: Long) {
|
||||
val messageDB = DatabaseFactory.getLokiMessageDatabase(context)
|
||||
val messageFRStatus = messageDB.getFriendRequestStatus(messageID)
|
||||
if (messageFRStatus == LokiMessageFriendRequestStatus.NONE || messageFRStatus == LokiMessageFriendRequestStatus.REQUEST_EXPIRED) {
|
||||
messageDB.setFriendRequestStatus(messageID, LokiMessageFriendRequestStatus.REQUEST_SENDING)
|
||||
}
|
||||
val threadDB = DatabaseFactory.getLokiThreadDatabase(context)
|
||||
val threadFRStatus = threadDB.getFriendRequestStatus(threadID)
|
||||
if (threadFRStatus == LokiThreadFriendRequestStatus.NONE || threadFRStatus == LokiThreadFriendRequestStatus.REQUEST_EXPIRED) {
|
||||
threadDB.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.REQUEST_SENDING)
|
||||
}
|
||||
}
|
||||
}
|
@ -2,33 +2,30 @@ package org.thoughtcrime.securesms.loki.protocol
|
||||
|
||||
import android.content.Context
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.sms.MessageSender
|
||||
import org.whispersystems.libsignal.loki.LokiSessionResetProtocol
|
||||
import org.whispersystems.libsignal.loki.LokiSessionResetStatus
|
||||
import org.whispersystems.libsignal.protocol.PreKeySignalMessage
|
||||
|
||||
class LokiSessionResetImplementation(private val context: Context) : LokiSessionResetProtocol {
|
||||
|
||||
override fun getSessionResetStatus(hexEncodedPublicKey: String): LokiSessionResetStatus {
|
||||
return DatabaseFactory.getLokiThreadDatabase(context).getSessionResetStatus(hexEncodedPublicKey)
|
||||
}
|
||||
|
||||
override fun setSessionResetStatus(hexEncodedPublicKey: String, sessionResetStatus: LokiSessionResetStatus) {
|
||||
return DatabaseFactory.getLokiThreadDatabase(context).setSessionResetStatus(hexEncodedPublicKey, sessionResetStatus)
|
||||
}
|
||||
|
||||
override fun onNewSessionAdopted(hexEncodedPublicKey: String, oldSessionResetStatus: LokiSessionResetStatus) {
|
||||
if (oldSessionResetStatus == LokiSessionResetStatus.IN_PROGRESS) {
|
||||
// Send a message back to the contact to finalise session reset
|
||||
MessageSender.sendBackgroundMessage(context, hexEncodedPublicKey)
|
||||
override fun getSessionResetStatus(hexEncodedPublicKey: String): LokiSessionResetStatus {
|
||||
return DatabaseFactory.getLokiThreadDatabase(context).getSessionResetStatus(hexEncodedPublicKey)
|
||||
}
|
||||
|
||||
// TODO: Show session reset succeed message
|
||||
}
|
||||
override fun setSessionResetStatus(hexEncodedPublicKey: String, sessionResetStatus: LokiSessionResetStatus) {
|
||||
return DatabaseFactory.getLokiThreadDatabase(context).setSessionResetStatus(hexEncodedPublicKey, sessionResetStatus)
|
||||
}
|
||||
|
||||
override fun validatePreKeySignalMessage(sender: String, message: PreKeySignalMessage) {
|
||||
val preKeyRecord = DatabaseFactory.getLokiPreKeyRecordDatabase(context).getPreKeyRecord(sender)
|
||||
check(preKeyRecord != null) { "Received a background message from a user without an associated pre key record." }
|
||||
check(preKeyRecord.id == (message.preKeyId ?: -1)) { "Received a background message from an unknown source." }
|
||||
}
|
||||
override fun onNewSessionAdopted(hexEncodedPublicKey: String, oldSessionResetStatus: LokiSessionResetStatus) {
|
||||
if (oldSessionResetStatus == LokiSessionResetStatus.IN_PROGRESS) {
|
||||
SessionMetaProtocol.sendEphemeralMessage(context, hexEncodedPublicKey)
|
||||
}
|
||||
// TODO: Show session reset succeed message
|
||||
}
|
||||
|
||||
override fun validatePreKeySignalMessage(sender: String, message: PreKeySignalMessage) {
|
||||
val preKeyRecord = DatabaseFactory.getLokiPreKeyRecordDatabase(context).getPreKeyRecord(sender)
|
||||
check(preKeyRecord != null) { "Received a background message from a user without an associated pre key record." }
|
||||
check(preKeyRecord.id == (message.preKeyId ?: -1)) { "Received a background message from an unknown source." }
|
||||
}
|
||||
}
|
@ -18,61 +18,59 @@ import javax.inject.Inject
|
||||
|
||||
class MultiDeviceOpenGroupUpdateJob private constructor(parameters: Parameters) : BaseJob(parameters), InjectableType {
|
||||
|
||||
companion object {
|
||||
const val KEY = "MultiDeviceOpenGroupUpdateJob"
|
||||
}
|
||||
|
||||
@Inject
|
||||
lateinit var messageSender: SignalServiceMessageSender
|
||||
|
||||
constructor() : this(Parameters.Builder()
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setQueue("MultiDeviceOpenGroupUpdateJob")
|
||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
||||
.setMaxAttempts(Parameters.UNLIMITED)
|
||||
.build())
|
||||
|
||||
override fun getFactoryKey(): String { return KEY
|
||||
}
|
||||
|
||||
override fun serialize(): Data { return Data.EMPTY }
|
||||
|
||||
@Throws(Exception::class)
|
||||
public override fun onRun() {
|
||||
if (!TextSecurePreferences.isMultiDevice(context)) {
|
||||
Log.d("Loki", "Not multi device; aborting...")
|
||||
return
|
||||
companion object {
|
||||
const val KEY = "MultiDeviceOpenGroupUpdateJob"
|
||||
}
|
||||
|
||||
val openGroups = mutableListOf<LokiPublicChat>()
|
||||
DatabaseFactory.getGroupDatabase(context).groups.use { reader ->
|
||||
while (true) {
|
||||
val record = reader.next ?: return@use
|
||||
if (!record.isOpenGroup) { continue; }
|
||||
@Inject
|
||||
lateinit var messageSender: SignalServiceMessageSender
|
||||
|
||||
val threadID = GroupManager.getThreadIDFromGroupID(record.encodedId, context)
|
||||
val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID)
|
||||
if (openGroup != null) { openGroups.add(openGroup) }
|
||||
}
|
||||
constructor() : this(Parameters.Builder()
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setQueue("MultiDeviceOpenGroupUpdateJob")
|
||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
||||
.setMaxAttempts(Parameters.UNLIMITED)
|
||||
.build())
|
||||
|
||||
override fun getFactoryKey(): String { return KEY }
|
||||
|
||||
override fun serialize(): Data { return Data.EMPTY }
|
||||
|
||||
@Throws(Exception::class)
|
||||
public override fun onRun() {
|
||||
if (!TextSecurePreferences.isMultiDevice(context)) {
|
||||
Log.d("Loki", "Not multi device; aborting...")
|
||||
return
|
||||
}
|
||||
// Gather open groups
|
||||
val openGroups = mutableListOf<LokiPublicChat>()
|
||||
DatabaseFactory.getGroupDatabase(context).groups.use { reader ->
|
||||
while (true) {
|
||||
val record = reader.next ?: return@use
|
||||
if (!record.isOpenGroup) { continue; }
|
||||
val threadID = GroupManager.getThreadIDFromGroupID(record.encodedId, context)
|
||||
val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID)
|
||||
if (openGroup != null) { openGroups.add(openGroup) }
|
||||
}
|
||||
}
|
||||
// Send the message
|
||||
if (openGroups.size > 0) {
|
||||
messageSender.sendMessage(SignalServiceSyncMessage.forOpenGroups(openGroups), UnidentifiedAccessUtil.getAccessForSync(context))
|
||||
} else {
|
||||
Log.d("Loki", "No open groups to sync.")
|
||||
}
|
||||
}
|
||||
|
||||
if (openGroups.size > 0) {
|
||||
messageSender.sendMessage(SignalServiceSyncMessage.forOpenGroups(openGroups), UnidentifiedAccessUtil.getAccessForSync(context))
|
||||
} else {
|
||||
Log.d("Loki", "No open groups to sync.")
|
||||
public override fun onShouldRetry(exception: Exception): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public override fun onShouldRetry(exception: Exception): Boolean {
|
||||
return false
|
||||
}
|
||||
override fun onCanceled() { }
|
||||
|
||||
override fun onCanceled() { }
|
||||
class Factory : Job.Factory<MultiDeviceOpenGroupUpdateJob> {
|
||||
|
||||
class Factory : Job.Factory<MultiDeviceOpenGroupUpdateJob> {
|
||||
|
||||
override fun create(parameters: Parameters, data: Data): MultiDeviceOpenGroupUpdateJob {
|
||||
return MultiDeviceOpenGroupUpdateJob(parameters)
|
||||
override fun create(parameters: Parameters, data: Data): MultiDeviceOpenGroupUpdateJob {
|
||||
return MultiDeviceOpenGroupUpdateJob(parameters)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,86 @@
|
||||
package org.thoughtcrime.securesms.loki.protocol
|
||||
|
||||
import android.content.Context
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
|
||||
object MultiDeviceProtocol {
|
||||
|
||||
@JvmStatic
|
||||
fun sendUnlinkingRequest(context: Context, publicKey: String) {
|
||||
val unlinkingRequest = EphemeralMessage.createUnlinkingRequest(publicKey)
|
||||
ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(unlinkingRequest))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun sendTextPush(context: Context, recipient: Recipient, messageID: Long) {
|
||||
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun sendMediaPush(context: Context, recipient: Recipient, messageID: Long) {
|
||||
|
||||
}
|
||||
|
||||
// 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<Job> jobs = new ArrayList<>();
|
||||
// for (Map.Entry<String, Boolean> 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<PushMediaSendJob>)(List)jobs);
|
||||
// } else {
|
||||
// // Schedule text send jobs
|
||||
// jobManager.startChain(jobs).enqueue();
|
||||
// }
|
||||
// });
|
||||
// return Unit.INSTANCE;
|
||||
// });
|
||||
// }
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
package org.thoughtcrime.securesms.loki.protocol
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
|
||||
import org.thoughtcrime.securesms.database.Address
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.jobmanager.Data
|
||||
import org.thoughtcrime.securesms.jobmanager.Job
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
||||
import org.thoughtcrime.securesms.jobs.BaseJob
|
||||
import org.thoughtcrime.securesms.logging.Log
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
|
||||
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) {
|
||||
|
||||
companion object {
|
||||
|
||||
const val KEY = "PushBackgroundMessageSendJob"
|
||||
|
||||
private val KEY_MESSAGE = "message"
|
||||
}
|
||||
|
||||
constructor(message: EphemeralMessage) : this(Parameters.Builder()
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setQueue(KEY)
|
||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
||||
.setMaxAttempts(1)
|
||||
.build(),
|
||||
message)
|
||||
|
||||
override fun serialize(): Data {
|
||||
return Data.Builder()
|
||||
.putString(KEY_MESSAGE, message.serialize())
|
||||
.build()
|
||||
}
|
||||
|
||||
override fun getFactoryKey(): String {
|
||||
return KEY
|
||||
}
|
||||
|
||||
public override fun onRun() {
|
||||
val recipient = message.get<String?>("recipient", null) ?: throw IllegalStateException()
|
||||
val dataMessage = SignalServiceDataMessage.newBuilder()
|
||||
.withTimestamp(System.currentTimeMillis())
|
||||
.withBody(message.get<String?>("body", null))
|
||||
// Attach a pre key bundle if needed
|
||||
if (message.get("friendRequest", false)) {
|
||||
val bundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(recipient)
|
||||
dataMessage.withPreKeyBundle(bundle).asFriendRequest(true)
|
||||
}
|
||||
// Set flags if needed
|
||||
if (message.get("unpairingRequest", false)) {
|
||||
dataMessage.asUnlinkingRequest(true)
|
||||
}
|
||||
if (message.get("sessionRestore", false)) {
|
||||
dataMessage.asSessionRestorationRequest(true)
|
||||
}
|
||||
if (message.get("sessionRequest", false)) {
|
||||
dataMessage.asSessionRequest(true)
|
||||
}
|
||||
// Send the message
|
||||
val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
|
||||
val address = SignalServiceAddress(recipient)
|
||||
try {
|
||||
val udAccess = UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(recipient), false))
|
||||
messageSender.sendMessage(-1, address, udAccess, dataMessage.build()) // The message ID doesn't matter
|
||||
} catch (e: Exception) {
|
||||
Log.d("Loki", "Failed to send background message to: $recipient due to error: $e.")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
public override fun onShouldRetry(e: Exception): Boolean {
|
||||
// Disable since we have our own retrying
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onCanceled() { }
|
||||
|
||||
class Factory : Job.Factory<PushEphemeralMessageSendJob> {
|
||||
|
||||
override fun create(parameters: Parameters, data: Data): PushEphemeralMessageSendJob {
|
||||
try {
|
||||
val messageJSON = data.getString(KEY_MESSAGE)
|
||||
return PushEphemeralMessageSendJob(parameters, EphemeralMessage.parse(messageJSON))
|
||||
} catch (e: IOException) {
|
||||
throw AssertionError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -22,4 +22,10 @@ object SessionManagementProtocol {
|
||||
ApplicationContext.getInstance(context).jobManager.add(CleanPreKeysJob())
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun sendSessionRestorationRequest(context: Context, publicKey: String) {
|
||||
val sessionRestorationRequest = EphemeralMessage.createSessionRestorationRequest(publicKey)
|
||||
ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(sessionRestorationRequest))
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
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.recipients.Recipient
|
||||
@ -8,6 +9,12 @@ import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendReque
|
||||
|
||||
object SessionMetaProtocol {
|
||||
|
||||
@JvmStatic
|
||||
fun sendEphemeralMessage(context: Context, publicKey: String) {
|
||||
val ephemeralMessage = EphemeralMessage.create(publicKey)
|
||||
ApplicationContext.getInstance(context).jobManager.add(PushEphemeralMessageSendJob(ephemeralMessage))
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be invoked for the recipient's master device.
|
||||
*/
|
||||
|
@ -1,10 +1,13 @@
|
||||
package org.thoughtcrime.securesms.loki.protocol
|
||||
|
||||
import android.content.Context
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor.NumberData
|
||||
import org.thoughtcrime.securesms.database.Address
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceGroupUpdateJob
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus
|
||||
@ -14,8 +17,8 @@ import java.util.*
|
||||
object SyncMessagesProtocol {
|
||||
|
||||
@JvmStatic
|
||||
fun shouldSyncReadReceipt(address: Address): Boolean {
|
||||
return !address.isGroup
|
||||
fun syncAllContacts(context: Context) {
|
||||
ApplicationContext.getInstance(context).jobManager.add(MultiDeviceContactUpdateJob(context, true))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@ -41,4 +44,19 @@ object SyncMessagesProtocol {
|
||||
val isFriend = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID) == LokiThreadFriendRequestStatus.FRIENDS
|
||||
return isFriend
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun syncAllClosedGroups(context: Context) {
|
||||
ApplicationContext.getInstance(context).jobManager.add(MultiDeviceGroupUpdateJob())
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun syncAllOpenGroups(context: Context) {
|
||||
ApplicationContext.getInstance(context).jobManager.add(MultiDeviceOpenGroupUpdateJob())
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun shouldSyncReadReceipt(address: Address): Boolean {
|
||||
return !address.isGroup
|
||||
}
|
||||
}
|
@ -33,127 +33,29 @@ import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||
import org.thoughtcrime.securesms.jobs.MmsSendJob;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceGroupUpdateJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushGroupSendJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushMediaSendJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushTextSendJob;
|
||||
import org.thoughtcrime.securesms.jobs.SmsSendJob;
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.loki.BackgroundMessage;
|
||||
import org.thoughtcrime.securesms.loki.FriendRequestHandler;
|
||||
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceOpenGroupUpdateJob;
|
||||
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
|
||||
import org.thoughtcrime.securesms.loki.PushBackgroundMessageSendJob;
|
||||
import org.thoughtcrime.securesms.loki.PushMessageSyncSendJob;
|
||||
import org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt;
|
||||
import org.thoughtcrime.securesms.loki.protocol.FriendRequestProtocol;
|
||||
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol;
|
||||
import org.thoughtcrime.securesms.mms.MmsException;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||
import org.thoughtcrime.securesms.push.AccountManagerFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
import org.whispersystems.signalservice.api.push.ContactTokenDetails;
|
||||
import org.whispersystems.signalservice.loki.protocol.multidevice.LokiDeviceLinkUtilities;
|
||||
import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus;
|
||||
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import kotlin.Unit;
|
||||
|
||||
public class MessageSender {
|
||||
|
||||
private static final String TAG = MessageSender.class.getSimpleName();
|
||||
|
||||
private enum MessageType { TEXT, MEDIA }
|
||||
|
||||
public static void syncAllContacts(Context context, Address recipient) {
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceContactUpdateJob(context, recipient, true));
|
||||
}
|
||||
|
||||
public static void syncAllGroups(Context context) {
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceGroupUpdateJob());
|
||||
}
|
||||
|
||||
public static void syncAllOpenGroups(Context context) {
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceOpenGroupUpdateJob());
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a contact sync message to all our devices telling them that we want to sync `contact`
|
||||
*/
|
||||
public static void syncContact(Context context, Address contact) {
|
||||
// Don't bother sending a contact sync message if it's one of our devices that we want to sync across
|
||||
MultiDeviceUtilities.isOneOfOurDevices(context, contact).success(isOneOfOurDevice -> {
|
||||
if (!isOneOfOurDevice) {
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceContactUpdateJob(context, contact));
|
||||
}
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
|
||||
public static void sendBackgroundMessageToAllDevices(Context context, String contactHexEncodedPublicKey) {
|
||||
// Send the background message to the original pubkey
|
||||
sendBackgroundMessage(context, contactHexEncodedPublicKey);
|
||||
|
||||
// Go through the other devices and only send background messages if we're friends or we have received friend request
|
||||
LokiDeviceLinkUtilities.INSTANCE.getAllLinkedDeviceHexEncodedPublicKeys(contactHexEncodedPublicKey).success(devices -> {
|
||||
Util.runOnMain(() -> {
|
||||
for (String device : devices) {
|
||||
// Don't send message to the device we already have sent to
|
||||
if (device.equals(contactHexEncodedPublicKey)) { continue; }
|
||||
Recipient recipient = Recipient.from(context, Address.fromSerialized(device), false);
|
||||
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(recipient);
|
||||
if (threadID < 0) { continue; }
|
||||
LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID);
|
||||
if (friendRequestStatus == LokiThreadFriendRequestStatus.FRIENDS || friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_RECEIVED) {
|
||||
sendBackgroundMessage(context, device);
|
||||
} else if (friendRequestStatus == LokiThreadFriendRequestStatus.NONE || friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_EXPIRED) {
|
||||
sendBackgroundFriendRequest(context, device, "Please accept to enable messages to be synced across devices");
|
||||
}
|
||||
}
|
||||
});
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
|
||||
// region Background message
|
||||
|
||||
// We don't call the message sender here directly and instead we just opt to create a specific job for the send
|
||||
// This is because calling message sender directly would cause the application to freeze in some cases as it was blocking the thread when waiting for a response from the send
|
||||
public static void sendBackgroundMessage(Context context, String contactHexEncodedPublicKey) {
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new PushBackgroundMessageSendJob(BackgroundMessage.create(contactHexEncodedPublicKey)));
|
||||
}
|
||||
|
||||
public static void sendBackgroundFriendRequest(Context context, String contactHexEncodedPublicKey, String messageBody) {
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new PushBackgroundMessageSendJob(BackgroundMessage.createFriendRequest(contactHexEncodedPublicKey, messageBody)));
|
||||
}
|
||||
|
||||
public static void sendUnpairRequest(Context context, String contactHexEncodedPublicKey) {
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new PushBackgroundMessageSendJob(BackgroundMessage.createUnpairingRequest(contactHexEncodedPublicKey)));
|
||||
}
|
||||
|
||||
public static void sendRestoreSessionMessage(Context context, String contactHexEncodedPublicKey) {
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new PushBackgroundMessageSendJob(BackgroundMessage.createSessionRestore(contactHexEncodedPublicKey)));
|
||||
}
|
||||
|
||||
public static void sendBackgroundSessionRequest(Context context, String contactHexEncodedPublicKey) {
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new PushBackgroundMessageSendJob(BackgroundMessage.createSessionRequest(contactHexEncodedPublicKey)));
|
||||
}
|
||||
// endregion
|
||||
|
||||
public static long send(final Context context,
|
||||
final OutgoingTextMessage message,
|
||||
final long threadId,
|
||||
@ -174,9 +76,9 @@ public class MessageSender {
|
||||
|
||||
long messageId = database.insertMessageOutbox(allocatedThreadId, message, forceSms, System.currentTimeMillis(), insertListener);
|
||||
|
||||
// Loki - Set the message's friend request status as soon as it has hit the database
|
||||
if (message.isFriendRequest) {
|
||||
FriendRequestHandler.updateFriendRequestState(context, FriendRequestHandler.ActionType.Sending, messageId, allocatedThreadId);
|
||||
// Loki - Set the message's friend request status as soon as it hits the database
|
||||
if (FriendRequestProtocol.shouldUpdateFriendRequestStatusFromOutgoingTextMessage(context, message)) {
|
||||
FriendRequestProtocol.setFriendRequestStatusToSendingIfNeeded(context, messageId, allocatedThreadId);
|
||||
}
|
||||
|
||||
sendTextMessage(context, recipient, forceSms, keyExchange, messageId);
|
||||
@ -190,73 +92,32 @@ public class MessageSender {
|
||||
final boolean forceSms,
|
||||
final SmsDatabase.InsertListener insertListener)
|
||||
{
|
||||
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
try {
|
||||
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
|
||||
long allocatedThreadId;
|
||||
long allocatedThreadId;
|
||||
|
||||
if (threadId == -1) {
|
||||
allocatedThreadId = threadDatabase.getThreadIdFor(message.getRecipient(), message.getDistributionType());
|
||||
} else {
|
||||
allocatedThreadId = threadId;
|
||||
}
|
||||
|
||||
Recipient recipient = message.getRecipient();
|
||||
|
||||
// Loki - Turn into a GIF message if possible
|
||||
if (message.getLinkPreviews().isEmpty() && message.getAttachments().isEmpty() && LinkPreviewUtil.isWhitelistedMediaUrl(message.getBody())) {
|
||||
new LinkPreviewRepository(context).fetchGIF(context, message.getBody(), attachmentOrNull -> Util.runOnMain(() -> {
|
||||
Attachment attachment = attachmentOrNull.orNull();
|
||||
try {
|
||||
if (attachment != null) { message.getAttachments().add(attachment); }
|
||||
long messageID = database.insertMessageOutbox(message, allocatedThreadId, forceSms, insertListener);
|
||||
// Loki - Set the message's friend request status as soon as it has hit the database
|
||||
if (message.isFriendRequest && !recipient.getAddress().isGroup() && !message.isGroup()) {
|
||||
FriendRequestHandler.updateFriendRequestState(context, FriendRequestHandler.ActionType.Sending, messageID, allocatedThreadId);
|
||||
}
|
||||
sendMediaMessage(context, recipient, forceSms, messageID, message.getExpiresIn());
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, e);
|
||||
// TODO: Handle
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
try {
|
||||
long messageID = database.insertMessageOutbox(message, allocatedThreadId, forceSms, insertListener);
|
||||
// Loki - Set the message's friend request status as soon as it has hit the database
|
||||
if (message.isFriendRequest && !recipient.getAddress().isGroup() && !message.isGroup()) {
|
||||
FriendRequestHandler.updateFriendRequestState(context, FriendRequestHandler.ActionType.Sending, messageID, allocatedThreadId);
|
||||
}
|
||||
sendMediaMessage(context, recipient, forceSms, messageID, message.getExpiresIn());
|
||||
} catch (MmsException e) {
|
||||
Log.w(TAG, e);
|
||||
return threadId;
|
||||
if (threadId == -1) {
|
||||
allocatedThreadId = threadDatabase.getThreadIdFor(message.getRecipient(), message.getDistributionType());
|
||||
} else {
|
||||
allocatedThreadId = threadId;
|
||||
}
|
||||
|
||||
Recipient recipient = message.getRecipient();
|
||||
long messageId = database.insertMessageOutbox(message, allocatedThreadId, forceSms, insertListener);
|
||||
|
||||
// Loki - Set the message's friend request status as soon as it hits the database
|
||||
if (FriendRequestProtocol.shouldUpdateFriendRequestStatusFromOutgoingMediaMessage(context, message)) {
|
||||
FriendRequestProtocol.setFriendRequestStatusToSendingIfNeeded(context, messageId, allocatedThreadId);
|
||||
}
|
||||
|
||||
sendMediaMessage(context, recipient, forceSms, messageId, message.getExpiresIn());
|
||||
return allocatedThreadId;
|
||||
} catch (MmsException e) {
|
||||
Log.w(TAG, e);
|
||||
return threadId;
|
||||
}
|
||||
|
||||
return allocatedThreadId;
|
||||
}
|
||||
|
||||
public static void sendSyncMessageToOurDevices(final Context context,
|
||||
final long messageID,
|
||||
final long timestamp,
|
||||
final byte[] message,
|
||||
final int ttl) {
|
||||
String ourPublicKey = TextSecurePreferences.getLocalNumber(context);
|
||||
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
|
||||
LokiDeviceLinkUtilities.INSTANCE.getAllLinkedDeviceHexEncodedPublicKeys(ourPublicKey).success(devices -> {
|
||||
Util.runOnMain(() -> {
|
||||
for (String device : devices) {
|
||||
// Don't send to ourselves
|
||||
if (device.equals(ourPublicKey)) { continue; }
|
||||
|
||||
// Create a send job for our device
|
||||
Address address = Address.fromSerialized(device);
|
||||
jobManager.add(new PushMessageSyncSendJob(messageID, address, timestamp, message, ttl));
|
||||
}
|
||||
});
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
|
||||
public static void resendGroupMessage(Context context, MessageRecord messageRecord, Address filterAddress) {
|
||||
@ -301,73 +162,11 @@ public class MessageSender {
|
||||
}
|
||||
|
||||
private static void sendTextPush(Context context, Recipient recipient, long messageId) {
|
||||
sendMessagePush(context, MessageType.TEXT, recipient, messageId);
|
||||
MultiDeviceProtocol.sendTextPush(context, recipient, messageId);
|
||||
}
|
||||
|
||||
private static void sendMediaPush(Context context, Recipient recipient, long messageId) {
|
||||
sendMessagePush(context, MessageType.MEDIA, recipient, messageId);
|
||||
}
|
||||
|
||||
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<Job> jobs = new ArrayList<>();
|
||||
for (Map.Entry<String, Boolean> 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<PushMediaSendJob>)(List)jobs);
|
||||
} else {
|
||||
// Schedule text send jobs
|
||||
jobManager.startChain(jobs).enqueue();
|
||||
}
|
||||
});
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
MultiDeviceProtocol.sendMediaPush(context, recipient, messageId);
|
||||
}
|
||||
|
||||
private static void sendGroupPush(Context context, Recipient recipient, long messageId, Address filterAddress) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user